All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.tinymediamanager.ui.tvshows.dialogs.TvShowSubtitleChooserDialog Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2012 - 2019 Manuel Laggner
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.tinymediamanager.ui.tvshows.dialogs;

import java.awt.BorderLayout;
import java.awt.Cursor;
import java.awt.Font;
import java.awt.Point;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.JScrollPane;
import javax.swing.JSeparator;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;

import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.tinymediamanager.core.entities.MediaFile;
import org.tinymediamanager.core.tasks.DownloadTask;
import org.tinymediamanager.core.threading.TmmTaskManager;
import org.tinymediamanager.core.tvshow.TvShowList;
import org.tinymediamanager.core.tvshow.TvShowModuleManager;
import org.tinymediamanager.core.tvshow.entities.TvShowEpisode;
import org.tinymediamanager.core.tvshow.tasks.TvShowSubtitleDownloadTask;
import org.tinymediamanager.scraper.MediaScraper;
import org.tinymediamanager.scraper.SubtitleSearchOptions;
import org.tinymediamanager.scraper.SubtitleSearchResult;
import org.tinymediamanager.scraper.entities.MediaLanguages;
import org.tinymediamanager.scraper.exceptions.MissingIdException;
import org.tinymediamanager.scraper.exceptions.ScrapeException;
import org.tinymediamanager.scraper.exceptions.UnsupportedMediaTypeException;
import org.tinymediamanager.scraper.mediaprovider.IMediaSubtitleProvider;
import org.tinymediamanager.ui.IconManager;
import org.tinymediamanager.ui.TableColumnResizer;
import org.tinymediamanager.ui.TmmFontHelper;
import org.tinymediamanager.ui.components.TmmLabel;
import org.tinymediamanager.ui.components.combobox.MediaScraperCheckComboBox;
import org.tinymediamanager.ui.components.table.TmmTable;
import org.tinymediamanager.ui.dialogs.MessageDialog;
import org.tinymediamanager.ui.dialogs.TmmDialog;
import org.tinymediamanager.ui.tvshows.TvShowSubtitleChooserModel;

import ca.odell.glazedlists.BasicEventList;
import ca.odell.glazedlists.EventList;
import ca.odell.glazedlists.GlazedLists;
import ca.odell.glazedlists.ObservableElementList;
import ca.odell.glazedlists.gui.AdvancedTableFormat;
import ca.odell.glazedlists.swing.DefaultEventTableModel;
import ca.odell.glazedlists.swing.GlazedListsSwing;
import net.miginfocom.swing.MigLayout;

/**
 * This dialog is used to show a chooser for subtitles found with the subtitle scrapers
 * 
 * @author Manuel Laggner
 */
public class TvShowSubtitleChooserDialog extends TmmDialog {
  private static final long                                  serialVersionUID = -3104541519073924724L;
  private static final Logger                                LOGGER           = LoggerFactory.getLogger(TvShowSubtitleChooserDialog.class);

  private final TvShowList                                   tvShowList       = TvShowList.getInstance();
  private final TvShowEpisode                                episodeToScrape;
  private final MediaFile                                    fileToScrape;
  private SearchTask                                         activeSearchTask = null;

  private EventList              subtitleEventList;
  private DefaultEventTableModel subtitleTableModel;

  private final boolean                                      inQueue;
  private boolean                                            continueQueue    = true;

  // UI components
  private JTable                                             tableSubs;
  private JComboBox                          cbLanguage;
  private MediaScraperCheckComboBox                          cbScraper;
  private JLabel                                             lblProgressAction;
  private JProgressBar                                       progressBar;
  private JButton                                            btnSearch;

  public TvShowSubtitleChooserDialog(TvShowEpisode episode, MediaFile mediaFile, boolean inQueue) {
    super(BUNDLE.getString("tvshowepisodesubtitlechooser.search"), "episodeSubtitleChooser"); //$NON-NLS-1$

    this.episodeToScrape = episode;
    this.fileToScrape = mediaFile;
    this.inQueue = inQueue;

    subtitleEventList = GlazedLists
        .threadSafeList(new ObservableElementList<>(new BasicEventList<>(), GlazedLists.beanConnector(TvShowSubtitleChooserModel.class)));
    subtitleTableModel = new DefaultEventTableModel<>(GlazedListsSwing.swingThreadProxyList(subtitleEventList), new SubtitleTableFormat());

    initComponents();

    // initializations
    LinkListener linkListener = new LinkListener();
    tableSubs.addMouseListener(linkListener);
    tableSubs.addMouseMotionListener(linkListener);
    tableSubs.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
    TableColumnResizer.adjustColumnPreferredWidths(tableSubs, 7);

    // Subtitle scraper
    List selectedSubtitleScrapers = new ArrayList<>();
    for (MediaScraper subtitleScraper : tvShowList.getAvailableSubtitleScrapers()) {
      if (TvShowModuleManager.SETTINGS.getSubtitleScrapers().contains(subtitleScraper.getId())) {
        selectedSubtitleScrapers.add(subtitleScraper);
      }
    }
    if (!selectedSubtitleScrapers.isEmpty()) {
      cbScraper.setSelectedItems(selectedSubtitleScrapers);
    }

    for (MediaLanguages language : MediaLanguages.valuesSorted()) {
      cbLanguage.addItem(language);
      if (language == TvShowModuleManager.SETTINGS.getSubtitleScraperLanguage()) {
        cbLanguage.setSelectedItem(language);
      }
    }

    // action listeners
    btnSearch.addActionListener(e -> searchSubtitle(fileToScrape.getFileAsPath().toFile(), episodeToScrape.getTvShow().getImdbId(),
        episodeToScrape.getSeason(), episodeToScrape.getEpisode()));
    cbLanguage.addActionListener(e -> searchSubtitle(fileToScrape.getFileAsPath().toFile(), episodeToScrape.getTvShow().getImdbId(),
        episodeToScrape.getSeason(), episodeToScrape.getEpisode()));

    // start initial search
    searchSubtitle(fileToScrape.getFileAsPath().toFile(), episodeToScrape.getTvShow().getImdbId(), episodeToScrape.getSeason(),
        episodeToScrape.getEpisode());
  }

  private void initComponents() {
    {
      final JPanel panelTitle = new JPanel();
      panelTitle.setLayout(new MigLayout("", "[grow]", "[]"));

      final JLabel lblEpisodeTitle = new JLabel(episodeToScrape.getTitle());
      TmmFontHelper.changeFont(lblEpisodeTitle, 1.33, Font.BOLD);
      panelTitle.add(lblEpisodeTitle, "cell 0 0 5 1,growx");

      setTopIformationPanel(panelTitle);
    }
    {
      final JPanel panelContent = new JPanel();
      getContentPane().add(panelContent, BorderLayout.CENTER);
      panelContent.setLayout(new MigLayout("", "[][][300lp,grow]", "[][][][][][shrink 0][200lp,grow]"));

      JLabel lblSeasonT = new TmmLabel(BUNDLE.getString("metatag.season")); //$NON-NLS-1$
      panelContent.add(lblSeasonT, "cell 0 0,alignx right");

      JLabel lblSeason = new JLabel(String.valueOf(episodeToScrape.getSeason()));
      panelContent.add(lblSeason, "cell 1 0");

      JLabel lblEpisodeT = new TmmLabel(BUNDLE.getString("metatag.episode")); //$NON-NLS-1$
      panelContent.add(lblEpisodeT, "cell 0 1,alignx right");

      JLabel lblEpisode = new JLabel(String.valueOf(episodeToScrape.getEpisode()));
      panelContent.add(lblEpisode, "cell 1 1");

      final JLabel lblMediaFileNameT = new TmmLabel(BUNDLE.getString("metatag.filename")); //$NON-NLS-1$
      panelContent.add(lblMediaFileNameT, "cell 0 2,alignx right");

      final JLabel lblMediaFileName = new JLabel(fileToScrape.getFilename());
      panelContent.add(lblMediaFileName, "cell 1 2 2 1,growx");

      final JLabel lblScraperT = new TmmLabel(BUNDLE.getString("scraper")); //$NON-NLS-1$
      panelContent.add(lblScraperT, "cell 0 3,alignx right");

      cbScraper = new MediaScraperCheckComboBox(tvShowList.getAvailableSubtitleScrapers());
      panelContent.add(cbScraper, "cell 1 3,growx");

      // $NON-NLS-1$
      btnSearch = new JButton(BUNDLE.getString("Button.search"));
      panelContent.add(btnSearch, "cell 2 3,alignx left");

      final JLabel lblLanguageT = new TmmLabel(BUNDLE.getString("metatag.language")); //$NON-NLS-1$
      panelContent.add(lblLanguageT, "cell 0 4,alignx right");

      cbLanguage = new JComboBox<>();
      panelContent.add(cbLanguage, "cell 1 4,growx");

      JSeparator separator = new JSeparator();
      panelContent.add(separator, "cell 0 5 3 1,growx");

      final JScrollPane scrollPaneSubs = new JScrollPane();
      panelContent.add(scrollPaneSubs, "cell 0 6 3 1,grow");

      tableSubs = new TmmTable(subtitleTableModel);
      scrollPaneSubs.setViewportView(tableSubs);
    }

    {
      {
        JPanel infoPanel = new JPanel();
        infoPanel.setLayout(new MigLayout("", "[][grow]", "[]"));

        progressBar = new JProgressBar();
        infoPanel.add(progressBar, "cell 0 0");

        lblProgressAction = new JLabel("");
        infoPanel.add(lblProgressAction, "cell 1 0");

        setBottomInformationPanel(infoPanel);
      }

      {
        if (inQueue) {
          JButton btnAbortQueue = new JButton(BUNDLE.getString("Button.abortqueue")); //$NON-NLS-1$
          btnAbortQueue.setIcon(IconManager.STOP_INV);
          btnAbortQueue.addActionListener(e -> {
            continueQueue = false;
            setVisible(false);
          });
          addButton(btnAbortQueue);
        }

        JButton btnDone = new JButton(BUNDLE.getString("Button.done")); //$NON-NLS-1$
        btnDone.setIcon(IconManager.APPLY_INV);
        btnDone.addActionListener(e -> setVisible(false));
        addDefaultButton(btnDone);
      }
    }
  }

  private void searchSubtitle(File file, String imdbId, int season, int episode) {
    if (activeSearchTask != null && !activeSearchTask.isDone()) {
      activeSearchTask.cancel();
    }

    // scrapers
    List scrapers = new ArrayList<>(cbScraper.getSelectedItems());

    activeSearchTask = new SearchTask(file, imdbId, season, episode, scrapers);
    activeSearchTask.execute();
  }

  private void startProgressBar(final String description) {
    SwingUtilities.invokeLater(() -> {
      lblProgressAction.setText(description);
      progressBar.setVisible(true);
      progressBar.setIndeterminate(true);
    });
  }

  private void stopProgressBar() {
    SwingUtilities.invokeLater(() -> {
      lblProgressAction.setText("");
      progressBar.setVisible(false);
      progressBar.setIndeterminate(false);
    });
  }

  /**
   * Shows the dialog and returns whether the work on the queue should be continued.
   *
   * @return true, if successful
   */
  public boolean showDialog() {
    // pack();
    // setLocationRelativeTo(MainWindow.getActiveInstance());
    setVisible(true);
    return continueQueue;
  }

  private class SearchTask extends SwingWorker {
    private File                       file;
    private int                        season;
    private int                        episode;
    private String                     imdbId;
    private List searchResults;
    private MediaLanguages             language;
    private List         scrapers;
    boolean                            cancel;

    SearchTask(File file, String imdbId, int season, int episode, List scrapers) {
      this.file = file;
      this.season = season;
      this.episode = episode;
      this.imdbId = imdbId;
      this.language = (MediaLanguages) cbLanguage.getSelectedItem();
      this.searchResults = new ArrayList<>();
      this.scrapers = scrapers;
      this.cancel = false;
    }

    @Override
    public Void doInBackground() {
      startProgressBar(BUNDLE.getString("chooser.searchingfor") + " " + episodeToScrape.getTitle()); //$NON-NLS-1$
      for (MediaScraper scraper : scrapers) {
        try {
          IMediaSubtitleProvider subtitleProvider = (IMediaSubtitleProvider) scraper.getMediaProvider();
          SubtitleSearchOptions options = new SubtitleSearchOptions(file);
          options.setImdbId(imdbId);
          options.setLanguage(language.toLocale());
          options.setSeason(season);
          options.setEpisode(episode);
          searchResults.addAll(subtitleProvider.search(options));
        }
        catch (ScrapeException e) {
          LOGGER.error("getSubtitles", e);
          MessageDialog.showExceptionWindow(e);
        }
        catch (MissingIdException | UnsupportedMediaTypeException ignored) {
        }
      }

      Collections.sort(searchResults);
      Collections.reverse(searchResults);

      return null;
    }

    public void cancel() {
      cancel = true;
    }

    @Override
    public void done() {
      if (!cancel) {
        subtitleEventList.clear();
        if (searchResults == null || searchResults.size() == 0) {
          // display empty result
          subtitleEventList.add(TvShowSubtitleChooserModel.EMPTY_RESULT);
        }
        else {
          for (SubtitleSearchResult result : searchResults) {
            subtitleEventList.add(new TvShowSubtitleChooserModel(result, language));
            // get metadataProvider from searchresult
          }
        }
        if (!subtitleEventList.isEmpty()) {
          tableSubs.setRowSelectionInterval(0, 0); // select first row
        }
        TableColumnResizer.adjustColumnPreferredWidths(tableSubs, 7);
      }
      stopProgressBar();
    }
  }

  private static class SubtitleTableFormat implements AdvancedTableFormat {
    @Override
    public int getColumnCount() {
      return 3;
    }

    @Override
    public String getColumnName(int column) {
      switch (column) {
        case 0:
          return "";

        case 1:
          return BUNDLE.getString("metatag.title"); //$NON-NLS-1$

        case 2:
          return BUNDLE.getString("metatag.releasename"); //$NON-NLS-1$

      }

      throw new IllegalStateException();
    }

    @Override
    public Object getColumnValue(TvShowSubtitleChooserModel model, int column) {
      switch (column) {
        case 0:
          return IconManager.DOWNLOAD;

        case 1:
          return model.getName();

        case 2:
          return model.getReleaseName();
      }

      throw new IllegalStateException();
    }

    @SuppressWarnings("rawtypes")
    @Override
    public Class getColumnClass(int column) {
      switch (column) {
        case 0:
          return ImageIcon.class;

        case 1:
        case 2:
          return String.class;
      }

      throw new IllegalStateException();
    }

    @SuppressWarnings("rawtypes")
    @Override
    public Comparator getColumnComparator(int arg0) {
      return null;
    }
  }

  private class LinkListener implements MouseListener, MouseMotionListener {
    @Override
    public void mouseClicked(MouseEvent e) {
      JTable table = (JTable) e.getSource();
      int row = table.rowAtPoint(new Point(e.getX(), e.getY()));
      int col = table.columnAtPoint(new Point(e.getX(), e.getY()));

      // click on the download button
      if (col == 0) {
        row = table.convertRowIndexToModel(row);
        TvShowSubtitleChooserModel model = subtitleEventList.get(row);

        if (StringUtils.isNotBlank(model.getDownloadUrl())) {
          String filename = FilenameUtils.getBaseName(fileToScrape.getFilename()) + "." + model.getLanguage().name();
          DownloadTask task = new TvShowSubtitleDownloadTask(model.getDownloadUrl(), episodeToScrape.getPathNIO().resolve(filename), episodeToScrape);
          TmmTaskManager.getInstance().addDownloadTask(task);
        }
      }
    }

    @Override
    public void mouseEntered(MouseEvent e) {
      JTable table = (JTable) e.getSource();
      int col = table.columnAtPoint(new Point(e.getX(), e.getY()));
      if (col == 0) {
        table.setCursor(new Cursor(Cursor.HAND_CURSOR));
      }
    }

    @Override
    public void mouseExited(MouseEvent e) {
      JTable table = (JTable) e.getSource();
      int col = table.columnAtPoint(new Point(e.getX(), e.getY()));
      if (col != 0) {
        table.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
      }
    }

    @Override
    public void mouseMoved(MouseEvent e) {
      JTable table = (JTable) e.getSource();
      int col = table.columnAtPoint(new Point(e.getX(), e.getY()));
      if (col != 0 && table.getCursor().getType() == Cursor.HAND_CURSOR) {
        table.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
      }
      if (col == 0 && table.getCursor().getType() == Cursor.DEFAULT_CURSOR) {
        table.setCursor(new Cursor(Cursor.HAND_CURSOR));
      }
    }

    @Override
    public void mousePressed(MouseEvent e) {
    }

    @Override
    public void mouseReleased(MouseEvent e) {
    }

    @Override
    public void mouseDragged(MouseEvent arg0) {
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy