private void process_removals() throws Exception {
    FileChange change = removalsToProcess.poll(1, TimeUnit.SECONDS);
    if (change == null) {
      return;
    }

    logger.fine("process removals: " + change.tree);

    String comp_path = change.tree.thisFile.getAbsolutePath();
    List<DownloadManager> toRemove = new ArrayList<DownloadManager>();
    GlobalManager gm = AzureusCoreImpl.getSingleton().getGlobalManager();
    for (DownloadManager dm : (List<DownloadManager>) gm.getDownloadManagers()) {
      if (dm.getSaveLocation().getAbsolutePath().startsWith(comp_path)) {
        logger.fine(
            "saveLocation.startsWith() delete of: "
                + dm.getSaveLocation().getAbsolutePath()
                + " / "
                + comp_path);
        toRemove.add(dm);
      } else if (comp_path.startsWith(dm.getSaveLocation().getAbsolutePath())) // deleted
      // a
      // file
      // in
      // the
      // torrent
      // directory
      // we
      // created,
      // need
      // to
      // remove
      // (and
      // possibly
      // rehash)
      {
        logger.fine(
            "comp_path.startsWith delete of: "
                + dm.getSaveLocation().getAbsolutePath()
                + " / "
                + comp_path);
        toRemove.add(dm);
      }
    }

    for (DownloadManager dm : toRemove) {
      logger.info("delete causes removal of: " + dm.getTorrentFileName());
      try {
        gm.removeDownloadManager(dm, true, false); // remove torrent
        // file (which we
        // created), but not
        // data
      } catch (Exception e) {
        e.printStackTrace();
      }
    }
  }
 /**
  * @param hash base32
  * @return true if this is one of our friend's files (i.e., we haven't started a download, but we
  *     want to show in the UI)
  */
 public boolean isF2FHash(String hash) {
   if (hash.startsWith(OneSwarmConstants.BITTORRENT_MAGNET_PREFIX)) {
     hash = hash.substring(OneSwarmConstants.BITTORRENT_MAGNET_PREFIX.length());
   }
   org.gudy.azureus2.core3.download.DownloadManager dm =
       AzureusCoreImpl.getSingleton()
           .getGlobalManager()
           .getDownloadManager(new HashWrapper(Base32.decode(hash)));
   return dm == null; // == null -> we don't have it -> f2f.
 }
  private MagicDirectoryManager() {
    logger.fine("MagicDirectoryManager()");

    watch_dirs = COConfigurationManager.getStringListParameter("Magic Watch Directories");
    if (watch_dirs == null) {
      watch_dirs = new StringListImpl();
    }

    for (int i = 0; i < watch_dirs.size(); i++) {
      logger.info(watch_dirs.get(i) + " wdir" + " " + watch_dirs.get(i).getClass().getName());
    }

    load_exclusions();

    AzureusCoreImpl.getSingleton()
        .addLifecycleListener(
            new AzureusCoreLifecycleListener() {
              public void componentCreated(AzureusCore core, AzureusCoreComponent component) {}

              public boolean restartRequested(AzureusCore core) throws AzureusCoreException {
                return true;
              }

              public void started(AzureusCore core) {}

              public boolean stopRequested(AzureusCore core) throws AzureusCoreException {
                stopping = true;
                return true;
              }

              public void stopped(AzureusCore core) {
                logger.fine("got stopped");
                stopping = true;
              }

              public void stopping(AzureusCore core) {
                logger.fine("got stopping");
                stopping = true;
              }

              public boolean syncInvokeRequired() {
                return false;
              }
            });

    // DEBUG
    // watch_dirs.add("/tmp/test/");
    COConfigurationManager.addParameterListener("Magic Watch Directories", this);

    setName("Magic directory scanner");
    setDaemon(true);
    start();
  }
  private void load_exclusions() {
    if (AzureusCoreImpl.isCoreAvailable() == false) {
      logger.warning("couldn't load exclusions: core not available yet!");
      return;
    }

    if (AzureusCoreImpl.getSingleton().isStarted() == false) {
      logger.warning("couldn't load exclusions: core not started yet!");
      return;
    }

    for (DownloadManager dm :
        (List<DownloadManager>)
            AzureusCoreImpl.getSingleton().getGlobalManager().getDownloadManagers()) {
      mExclusions.add(dm.getSaveLocation().getAbsolutePath());
    }

    AzureusCoreImpl.getSingleton()
        .getGlobalManager()
        .addListener(
            new GlobalManagerListener() {
              public void destroyInitiated() {}

              public void destroyed() {}

              public void downloadManagerAdded(DownloadManager dm) {
                mExclusions.add(dm.getSaveLocation().getAbsolutePath());
              }

              public void downloadManagerRemoved(DownloadManager dm) {}

              public void seedingStatusChanged(boolean seeding_only_mode) {}
            });

    synchronized (mExclusions) {
      for (String s : mExclusions) {
        logger.finest("exclusion: " + s);
      }
    }
  }
  public static void autotag_existing_audio() {

    final MutableBoolean stopIt = new MutableBoolean(false);
    int ourID =
        BackendTaskManager.get()
            .createTask(
                "Auto-tagging audio files...",
                new CancellationListener() {
                  public void cancelled(int inID) {
                    synchronized (stopIt) {
                      stopIt.val = true;
                    }
                  }
                });
    List<DownloadManager> managers =
        (List<DownloadManager>)
            AzureusCoreImpl.getSingleton().getGlobalManager().getDownloadManagers();
    for (int i = 0; i < managers.size(); i++) {
      DownloadManager dm = managers.get(i);

      BackendTaskManager.get()
          .getTask(ourID)
          .setProgress(Math.round(((double) i / (double) managers.size()) * 100.0) + "%");

      if (dm.getDownloadState() == null) {
        logger.warning("null download state for: " + dm.getDisplayName());
        continue;
      }

      synchronized (stopIt) {
        if (stopIt.val == true) {
          break;
        }
      }

      if (dm.getDownloadState().getAttribute(FileCollection.ONESWARM_TAGS_ATTRIBUTE) == null) {
        bind_audio_xml(dm, true);
      } // if no current tags
    }
    BackendTaskManager.get().removeTask(ourID);
  }
 public static String bind_audio_scan() {
   long start = System.currentTimeMillis();
   StringBuilder out = new StringBuilder();
   out.append("bind audio scan...");
   String metainfoPath = SystemProperties.getMetaInfoPath();
   UpdatingFileTree metainfo = new UpdatingFileTree(new File(metainfoPath));
   List<UpdatingFileTree> q = new ArrayList<UpdatingFileTree>();
   q.add(metainfo);
   while (q.isEmpty() == false) {
     UpdatingFileTree curr = q.remove(0);
     if (curr.isDirectory()) {
       q.addAll(curr.getChildren());
     } else if (curr.getThisFile().getName().equals(PreviewImageGenerator.AUDIO_INFO_FILE)) {
       String hashStr = curr.getThisFile().getParentFile().getName();
       byte[] hashBytes = Base32.decode(hashStr);
       DownloadManager dm =
           AzureusCoreImpl.getSingleton()
               .getGlobalManager()
               .getDownloadManager(new HashWrapper(hashBytes));
       // old metainfo and/or bogus directory name
       if (dm == null) {
         continue;
       }
       logger.finest("rebind audio considering: " + dm.getDisplayName());
       out.append("considered: " + dm.getDisplayName());
       if (dm.getDownloadState().getAttribute(FileCollection.ONESWARM_ARTIST_ATTRIBUTE) == null
           && dm.getDownloadState().getAttribute(FileCollection.ONESWARM_ALBUM_ATTRIBUTE)
               == null) {
         logger.finer("should rebind audio " + dm.getDisplayName());
         out.append("rebinding for: " + dm.getDisplayName());
         bind_audio_xml(dm);
       }
     }
   }
   String str = "binding audio scan took: " + (System.currentTimeMillis() - start);
   logger.info(str);
   out.append(str);
   return out.toString();
 }
  private void create_swarm_synchronously(final File file, String[] tags) throws Exception {
    /** Trying to create these causes a TOTorrentException */
    if (file.isDirectory() == false) {
      if (file.length() == 0) {
        return;
      }
    }

    TOTorrentCreator currentCreator = null;
    cancelled = false;
    try {
      currentCreator =
          TOTorrentFactory.createFromFileOrDirWithComputedPieceLength(
              file, new URL("http://tracker.invalid/announce"), true);
    } catch (java.net.MalformedURLException e) {
      throw new TOTorrentException("malformed tracker url (should _never_ happen)", 0);
    }
    final TOTorrentCreator creator_shadow = currentCreator;

    final BackendTaskManager tasks = BackendTaskManager.get();
    final int task_id =
        tasks.createTask(
            "Hashing...",
            new CancellationListener() {
              public void cancelled(int inID) {
                mExclusions.add(file.getAbsolutePath());
                creator_shadow.cancel();
              }
            });
    tasks.getTask(task_id).setSummary("Watch directory hash: " + file.getName());

    currentCreator.addListener(
        new TOTorrentProgressListener() {
          public void reportCurrentTask(String task_description) {
            System.out.println("creating: " + task_description);
          }

          public void reportProgress(int percent_complete) {
            if ((percent_complete % 10) == 0) {
              logger.fine("progress: " + percent_complete);
            }

            if (tasks.getTask(task_id) != null) {
              tasks.getTask(task_id).setProgress(percent_complete + "%");
            }

            if (stopping && !cancelled) {
              creator_shadow.cancel();
              cancelled = true;
            }
          }
        });
    TOTorrent created = null;
    try {
      created = currentCreator.create();
    } catch (TOTorrentException e) {
      if (e.getReason() == TOTorrentException.RT_ZERO_LENGTH) {
        logger.warning("Skipping creation of zero-length swarm: " + file.getAbsolutePath());
        return;
      }
      throw e;
    }
    logger.finer("create finished, removing task_id: " + task_id);
    tasks.removeTask(task_id);
    if (created == null || cancelled) {
      System.err.println("created == null, canceled?");
      return;
    }

    String configSavePath =
        COConfigurationManager.getStringParameter("General_sDefaultTorrent_Directory");
    File outTorrent = null;
    if (configSavePath == null) {
      outTorrent = new File(file.getParentFile().getAbsolutePath(), file.getName() + ".torrent");
    } else {
      outTorrent = new File(configSavePath, file.getName() + ".torrent");
    }
    logger.finer("saving to: " + outTorrent.getAbsolutePath());

    try {
      LocaleTorrentUtil.setDefaultTorrentEncoding(created);
    } catch (LocaleUtilEncodingException e1) {
      e1.printStackTrace();
    }

    logger.finer("setdefaultencoding, serializing...");

    created.serialiseToBEncodedFile(outTorrent);

    logger.finest("done that");

    /**
     * very small chance of this happening -- most of the time the quit will come during the hashing
     * (which will cancel it, which will result in null and immediate return)
     */
    if (!stopping) {
      generate_preview_for_torrent(created, file);

      logger.finer("settings perms");
      ArrayList<GroupBean> typed = new ArrayList<GroupBean>();
      typed.add(GroupBean.ALL_FRIENDS);
      PermissionsDAO.get()
          .setGroupsForHash(ByteFormatter.encodeString(created.getHash()), typed, true);

      /** Finally add that swarm and make sure the permissions are f2f only */
      GlobalManager gm = AzureusCoreImpl.getSingleton().getGlobalManager();

      logger.finer(
          "calling add download manager, file: "
              + file.getAbsolutePath()
              + " save: "
              + file.getParentFile().getAbsolutePath());

      try {
        final DownloadManager dm =
            gm.addDownloadManager(
                outTorrent.getAbsolutePath(),
                created.getHash(),
                file.getAbsolutePath(),
                org.gudy.azureus2.core3.download.DownloadManager.STATE_WAITING,
                true,
                true,
                null);

        if (tags != null) {
          DownloadManagerState dmState = dm.getDownloadState();
          if (dmState != null) {
            dm.getDownloadState().setListAttribute(FileCollection.ONESWARM_TAGS_ATTRIBUTE, tags);
            logger.finer("set tags: " + tags.length + " first: " + tags[0]);
          }
        }

        dm.addListener(
            new DownloadManagerListener() {
              public void completionChanged(DownloadManager manager, boolean completed) {}

              public void downloadComplete(DownloadManager manager) {}

              public void filePriorityChanged(DownloadManager download, DiskManagerFileInfo file) {}

              public void positionChanged(
                  DownloadManager download, int oldPosition, int newPosition) {}

              public void stateChanged(DownloadManager manager, int state) {
                if (state == org.gudy.azureus2.core3.download.DownloadManager.STATE_SEEDING) {
                  logger.fine("binding audio data for: " + dm.getDisplayName());
                  MagicDirectoryManager.bind_audio_xml(dm);

                  dm.stopIt(
                      org.gudy.azureus2.core3.download.DownloadManager.STATE_STOPPED, false, false);
                  dm.removeListener(this);
                }
              }
            });

        dm.setForceStart(true);
      } catch (Exception e) {
        logger.warning(e.toString());
        e.printStackTrace();
        throw e;
      }

      // logger.finest("force start");
      // dm.setForceStart(true);
    } else {
      logger.finer("was stopping");
    }

    // coreInterface.getF2FInterface().setTorrentPrivacy(dm.getTorrent().getHash(),
    // false, true);
    // start doesn't matter here, f2f should start it automatically if
    // there's a request
  }
  public static void bind_audio_xml(DownloadManager real_dl, boolean force) {
    try {
      /** Don't do this multiple times. */
      if (real_dl.getDownloadState() != null && force == false) {
        if (real_dl.getDownloadState().getAttribute(FileCollection.ONESWARM_ALBUM_ATTRIBUTE)
            != null) {
          return;
        }
        if (real_dl.getDownloadState().getAttribute(FileCollection.ONESWARM_ARTIST_ATTRIBUTE)
            != null) {
          return;
        }
      }

      File metaInfoDir = CoreInterface.getMetaInfoDir(real_dl.getTorrent().getHash());
      File audio_xml = new File(metaInfoDir, PreviewImageGenerator.AUDIO_INFO_FILE);
      if (audio_xml.exists() == false) {
        logger.finest(
            "no xml data for: " + real_dl.getDisplayName() + " / skipping audio info bind");
        return;
      }

      XMLDecoder decoder = new XMLDecoder(new BufferedInputStream(new FileInputStream(audio_xml)));
      Map<String, Properties> audio_file_properties =
          (Map<String, Properties>) decoder.readObject();
      decoder.close();

      boolean setArtist = false, setAlbum = false;
      for (Properties p : audio_file_properties.values()) {
        for (String attrib : new String[] {FileCollection.ONESWARM_ARTIST_ATTRIBUTE}) {
          if (p.getProperty(attrib) != null) {
            real_dl
                .getDownloadState()
                .setAttribute(FileCollection.ONESWARM_ARTIST_ATTRIBUTE, p.getProperty(attrib));
            logger.fine(
                "bound artist: " + p.getProperty(attrib) + " for " + real_dl.getDisplayName());
            setArtist = true;

            if (COConfigurationManager.getBooleanParameter("oneswarm.add.id3.tags")) {
              logger.fine(
                  "adding id3 tag for album: " + p.getProperty(attrib).replaceAll("/", "-"));
              String[] tags =
                  real_dl
                      .getDownloadState()
                      .getListAttribute(FileCollection.ONESWARM_TAGS_ATTRIBUTE);
              if (tags == null) {
                tags = new String[0];
              }
              String[] neu = new String[tags.length + 1];
              System.arraycopy(tags, 0, neu, 0, tags.length);
              neu[tags.length] = "Artists/" + p.getProperty(attrib).replaceAll("/", "-");
              real_dl
                  .getDownloadState()
                  .setListAttribute(FileCollection.ONESWARM_TAGS_ATTRIBUTE, neu);
            }

            break;
          }
        }

        for (String attrib : new String[] {FileCollection.ONESWARM_ALBUM_ATTRIBUTE}) // to
        // deal
        // with
        // old
        // versions
        {
          if (p.getProperty(attrib) != null) {
            real_dl
                .getDownloadState()
                .setAttribute(FileCollection.ONESWARM_ALBUM_ATTRIBUTE, p.getProperty(attrib));
            logger.fine(
                "bound album: " + p.getProperty(attrib) + " for " + real_dl.getDisplayName());
            setAlbum = true;

            if (COConfigurationManager.getBooleanParameter("oneswarm.add.id3.tags")) {
              logger.fine(
                  "adding id3 tag for album: " + p.getProperty(attrib).replaceAll("/", "-"));
              String[] tags =
                  real_dl
                      .getDownloadState()
                      .getListAttribute(FileCollection.ONESWARM_TAGS_ATTRIBUTE);
              if (tags == null) {
                tags = new String[0];
              }
              String[] neu = new String[tags.length + 1];
              System.arraycopy(tags, 0, neu, 0, tags.length);
              neu[tags.length] = "Albums/" + p.getProperty(attrib).replaceAll("/", "-");
              real_dl
                  .getDownloadState()
                  .setListAttribute(FileCollection.ONESWARM_TAGS_ATTRIBUTE, neu);
            }

            break;
          }
        }
        if (setArtist && setAlbum) {
          break;
        }
      }

      /** Need to regenerate our file list to incorporate this new info */
      if (setArtist || setAlbum) {
        PluginInterface f2fIf =
            AzureusCoreImpl.getSingleton().getPluginManager().getPluginInterfaceByID("osf2f");
        if (f2fIf != null) {
          if (f2fIf.isOperational() == true) {
            IPCInterface ipc = f2fIf.getIPC();
            if (ipc != null) {
              ipc.invoke("refreshFileLists", new Object[0]);
            } else {
              logger.warning("f2f IPC is null, couldn't regenerate file list after audio binding");
            }
          } else {
            logger.warning(
                "Couldn't regenerate file list after audio info binding, f2f plugin is not operational");
          }
        } else {
          logger.warning(
              "Couldn't regenerate file list after audio info binding, f2f plugin interface is null");
        }
      }

    } catch (Exception e) {
      logger.warning(
          "error binding audio xml to download manager: "
              + real_dl.getDisplayName()
              + " / "
              + e.toString());
    }
  }