From 916f168d988b23831b73fb98cc30a8a8ee72a893 Mon Sep 17 00:00:00 2001 From: Reinhard Pointner Date: Wed, 21 Oct 2009 22:39:02 +0000 Subject: [PATCH] + added support for subtitle download via video/movie hash * added video/subtitle file drop target in SubtitlePanel * added VideoHashSubtitleDownloadDialog --- fw/database.ok.png | Bin 0 -> 23433 bytes fw/subtitle.exact.download.png | Bin 0 -> 36348 bytes fw/subtitle.exact.upload.png | Bin 0 -> 36554 bytes .../filebot/resources/arrow.down.png | Bin 0 -> 180 bytes .../filebot/resources/database.error.png | Bin 0 -> 682 bytes .../filebot/resources/database.go.png | Bin 0 -> 698 bytes .../filebot/resources/database.ok.png | Bin 0 -> 689 bytes .../resources/subtitle.exact.download.png | Bin 0 -> 1759 bytes .../resources/subtitle.exact.upload.png | Bin 0 -> 1719 bytes .../filebot/ui/AbstractSearchPanel.java | 8 +- .../panel/episodelist/EpisodeListPanel.java | 4 +- .../ui/panel/rename/HistoryDialog.java | 2 +- .../ui/panel/subtitle/SimpleComboBox.java | 70 ++ .../ui/panel/subtitle/SubtitleDropTarget.java | 238 ++++++ .../ui/panel/subtitle/SubtitlePanel.java | 73 +- .../ui/panel/subtitle/SubtitleServices.java | 41 + .../VideoHashSubtitleDownloadDialog.java | 804 ++++++++++++++++++ .../filebot/web/OpenSubtitlesClient.java | 6 + .../filebot/web/SublightSubtitleClient.java | 11 +- .../filebot/web/SubsceneSubtitleClient.java | 6 + .../filebot/web/SubtitleProvider.java | 3 + .../filebot/web/SubtitleSourceClient.java | 10 +- .../filebot/web/VideoHashSubtitleService.java | 12 + .../net/sourceforge/tuned/FileUtilities.java | 26 + .../sourceforge/tuned/ui/AbstractBean.java | 46 + .../net/sourceforge/tuned/ui/RoundBorder.java | 73 ++ 26 files changed, 1405 insertions(+), 28 deletions(-) create mode 100644 fw/database.ok.png create mode 100644 fw/subtitle.exact.download.png create mode 100644 fw/subtitle.exact.upload.png create mode 100644 source/net/sourceforge/filebot/resources/arrow.down.png create mode 100644 source/net/sourceforge/filebot/resources/database.error.png create mode 100644 source/net/sourceforge/filebot/resources/database.go.png create mode 100644 source/net/sourceforge/filebot/resources/database.ok.png create mode 100644 source/net/sourceforge/filebot/resources/subtitle.exact.download.png create mode 100644 source/net/sourceforge/filebot/resources/subtitle.exact.upload.png create mode 100644 source/net/sourceforge/filebot/ui/panel/subtitle/SimpleComboBox.java create mode 100644 source/net/sourceforge/filebot/ui/panel/subtitle/SubtitleDropTarget.java create mode 100644 source/net/sourceforge/filebot/ui/panel/subtitle/SubtitleServices.java create mode 100644 source/net/sourceforge/filebot/ui/panel/subtitle/VideoHashSubtitleDownloadDialog.java create mode 100644 source/net/sourceforge/tuned/ui/AbstractBean.java create mode 100644 source/net/sourceforge/tuned/ui/RoundBorder.java diff --git a/fw/database.ok.png b/fw/database.ok.png new file mode 100644 index 0000000000000000000000000000000000000000..2f8a72187537078865104395bd26e666cc3c6e55 GIT binary patch literal 23433 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 literal 0 HcmV?d00001 diff --git a/fw/subtitle.exact.download.png b/fw/subtitle.exact.download.png new file mode 100644 index 0000000000000000000000000000000000000000..1887340dc06a37840c28884e0f0f443a95b84342 GIT binary patch literal 36348 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
    45n0a(u)_OQ*fp)5Qq}c7QWMET~@_8~a8pldP}z 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 literal 0 HcmV?d00001 diff --git a/fw/subtitle.exact.upload.png b/fw/subtitle.exact.upload.png new file mode 100644 index 0000000000000000000000000000000000000000..1405bb794ec30244fc5332b8cb16deccacf9a912 GIT binary patch literal 36554 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�jLoA}T7( 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 I1H>cozbqi)bN~PV literal 0 HcmV?d00001 diff --git a/source/net/sourceforge/filebot/resources/arrow.down.png b/source/net/sourceforge/filebot/resources/arrow.down.png new file mode 100644 index 0000000000000000000000000000000000000000..3bc773450eb8d73ff92b380fa6d69e2b7980abd7 GIT binary patch literal 180 zcmeAS@N?(olHy`uVBq!ia0vp^tU%1n!3HFw&5r&6QY^(zo*^7SP{WbZ0pxQQctjR6 zFmMZjFyp1Wb$@_@@+GbjCBgY=CFO}lsSLh}$wm3OsVSL>3T~N2spa`a*~JPLTQe?I z0F`Qcx;Tb#%=A66Q1E~P1H(p{R;(_gvD%^j>M0D71~-eDzv6DALJ4i U<^At-9H@=K)78&qol`;+0FHk&xc~qF literal 0 HcmV?d00001 diff --git a/source/net/sourceforge/filebot/resources/database.error.png b/source/net/sourceforge/filebot/resources/database.error.png new file mode 100644 index 0000000000000000000000000000000000000000..578221aaab1c7f6f1f5f9eb4adf02aaad93bf407 GIT binary patch literal 682 zcmV;b0#*HqP)87 z5D7ClS`>i>siY*D_Hz7r-uLe7+&8^)8l^=KKF+=8o%5aVo_ns%Fbw?9=NR3j(`lIq z&l4_j&X15iM4%;qPxcepJA`yB7SpX9o27t~=V*Q3@ApT8!5};y58Q4y#D7Im(CKtg zsZ`KtG+vYMVmuzNnu#JnAeBlzFPF=roH6NRj2XFH&PXPcD^{M}l0uuKfk5D&XAXrz zpdx3t^?;lvW4&I7%jJSB%WyiKrmxrQq19@kTCE}+4x6zP2^(duC2SE#)bB9=iVf68`kSQeakO%!CNk8ZR-m z?*w*FFTmM-3w@)sMZq=#Gl@t{bZZ!XeF5)YAM_uOFn#O_`sF9!I-5*jRo65I^~*E( zBNu?dI#%!A!!BPRgIX3|y(Et%5U{H2%16kyA-q#PaP>1aiIe;_%$~lEAY1pW-a6^s zLLj7ztD;GSyh8%7JcV!OIC$?J<@T`{kAM$r)ZhXRM9)c@I=uF`f~8C*(=8T@aDLBH z?I;- zi%TAvMLVRO5!65AQFy%kz&7qVzqz^CG<5E!W7`eUT(0v?OSAIJVzcSlpH|(e&rbmL QkpKVy07*qoM6N<$f|gM=-T(jq literal 0 HcmV?d00001 diff --git a/source/net/sourceforge/filebot/resources/database.go.png b/source/net/sourceforge/filebot/resources/database.go.png new file mode 100644 index 0000000000000000000000000000000000000000..61a8556c403a1b56cb7719f4916d07ed82cd55ff GIT binary patch literal 698 zcmV;r0!96aP)U4c>+hFP*|&0tGt{!YG|5fRw@-UpUxK})gOY{KjH!sGG4?RHzSe!mY<6j86&kw_%0+>rt+UvTG zPNxG!QLHl_`>TN6lhf(69Pnfg>e~`ot!6OL_K}%<0mC@>2*2;ZcEFQ4iGwF{@R*{j z7!M|qd3hZgQye2(un7;}EWl(MRHj3v{mH--l93DO%KKRTaX^3MX`58z^<+{ z6<23&!!Q{PW`ItyBW>gC_$Bnz0p8cvrP&8U;E(_Zug)QpWlpZPzmjE&ksHm>&{4WL zcWMqjtuMUYDzy&;xOM)i=ubqW(I5dCx}hU{e1gb^CAKTo5EzT#!}bO?zL#36j`?8+ z3*~b8c`}*w$6_%IbOkHrWx4~^auW|u<6?Xs@2VxNZ5G?Ij>|hs<|oJSYs}?xlO%MH zkQM~t1b+*>9q#P0c*i_HG3Qv{e6_1E^9qr_9C}QDj%+r2jL4@6j4t)_BWY1InA104 gM*QcJxn<}50%n)c1HutrKL7v#07*qoM6N<$f)5Qpt^fc4 literal 0 HcmV?d00001 diff --git a/source/net/sourceforge/filebot/resources/database.ok.png b/source/net/sourceforge/filebot/resources/database.ok.png new file mode 100644 index 0000000000000000000000000000000000000000..3188a5c36368346a2658b579de4cdf2a402281b5 GIT binary patch literal 689 zcmV;i0#5yjP)rk!!3SPE@|g$5x7jo^n6 z5#0#I3hK(0pP)+@ehQ(2iWF?oPf&=suvSEA{S&o{1tV#pv`HM(rZai>-Q#LvN?Kfa zckgk}@11+zdk#tbPkFtIa=Gl2yiD@Cs-7Y_K~hzhIF3OQB7!K2kR%B_&jZPW0qIPcOa?&^!1FwKo(E$LLqM*4pl#>$-9`~JSWrx3el2kp)-L=6&&VcWJZgPTiB zOHWFrQnpYi!1sMPjsuc7w{sppnrp}($sv{)t>`z*cIPS3^A`YMc6Rops@_)BE3WGv z55o{5g7sz%6?wn2CSQ}iy>>mR$8)x6?tOmvb7LQ%w`OK$j)_Q7MAR6wIz2s|9Mty* X>D{tX`+6by00000NkvXXu0mjfJpL=d literal 0 HcmV?d00001 diff --git a/source/net/sourceforge/filebot/resources/subtitle.exact.download.png b/source/net/sourceforge/filebot/resources/subtitle.exact.download.png new file mode 100644 index 0000000000000000000000000000000000000000..eb76b42cc5265475db596421f67bbe3bc8bdc1b7 GIT binary patch literal 1759 zcmV<51|a!~P) zTk7KvtHd--wVK^VO$a2-uK#G7R7`7){!sxRF;!6dhbA_fT^}WBO@!6d{?&9FqoG>t z8ZoqDc{I=vUVE2)+{etE;~)3_Uce%gd^0oW%$@H&XU;iuuj${Lax-n)8ip3bRx1od z%7;+u3W$;j{L>jGXM~A^mpODZ`j?KwSU?lnPoc)Ye=f znUnDJkv#1G%cIMZs|nZc%K)$oz0qUO=0kTYC&}4W1nRkiJcqP^EH9B4kQIgZ zeVN7whxbj7-W2g;zsyE)3}>#5tg^DGta5EA0=Q6)^9s%z3UA0gn85J%r>3qU?;w8k znQT>46ep)%72qGo1s3iXm$)>Eg zKZyZW&9XBusJqHd-oqE}jrr*NJ1yQdnzLz*W%$#z4*Bn1_Ply5@Z`hY9rB~6gl~=J zs9IDN38B7TdUhU%J*Ize%DrBvRLhhp9H_wNTP#Z|iyMJ(^ev&U*RZ;`)8Z5FlnK3Q zZ7FZ$`ME?nmy|P+fP?~7N#0n!wAT#&BptMKddP>8)Q9pZQ~Atj} zV7JCu#()OMU3s=DFn&0&<5M+m?fZW=-nhN~mhkok#f8!U5s;t=(Lh`j42ZUJRJ3&O zQ_mNCdwUnFdkxOROsY74VriW*-b zKk9mXm|O0%!d+f-*>0AD#!Fg4fsyo57T z%6DFH&A4dAiJ}y!WT>QoLrpT=wf2tbCNY3p9*-007%0YQo7JWRv(B*ZCC_a2Y4rS& z!0F`bb$~jM95gM*){XVte&=|Duj8A`tgMZgm@y)$?nMDB5NU@SfBRbC*g4_Sobtn$ ze49?egfhD&1DOXcFxHQCZ{9T0&UMX&W>?lmk%+q3R*HL|ii5vw2{g z>$C;`B%&2d$BC|NRI=SzNx+w9rFrGX3_^;^JSuY_X{oc45N3B1&u(0Z^oAB+-Tb9< zgAi@S*a3qb0P3KqYJ$2+_4%YqKGD^G`+{Wc?SHi>{lFyO4*dWC002ovPDHLkV1kr~ BS>*r# literal 0 HcmV?d00001 diff --git a/source/net/sourceforge/filebot/resources/subtitle.exact.upload.png b/source/net/sourceforge/filebot/resources/subtitle.exact.upload.png new file mode 100644 index 0000000000000000000000000000000000000000..bb64b9317194fe51c06685390ca4a214db091f9d GIT binary patch literal 1719 zcmV;o21xmdP)rwUp297=zmOe-Yv*IaqEQ*M1HEwP~gNigt6w0aU;F6x{dB3(T1A4o` z8=p?iE{6JbZ=@jBBMphw2w1u_vLH>3z~k}WBc8%?pMQOMZ+>=1z&Adbnl(@k6CjB? z7EUy>a5(%dkA$BiWfLZv^G5I@cyWYIu$uDCk=`xfVduy7hf=c^$|Q?~PB0xb0vI5t zdicS>If}su6W8t|l896n?Ly$p0Zf*4MFMe(;?sV8cLR6{{S zfl(-t0IvBit9SEg#7R5nBGd=mti0D(XaO(oQpb&@ z5SexniHjAHRz%w8cD^!FVCI2#)XhL7K9uMU2#>sk$?b{aDlQbr0oYayEeHP3Hggdl>k;A4Di&oKs4 zgKW5UX5V>*_iiu5wjKJ9>f5}w@G7hJHigt6-?{raM)YvJPBVQGq>bRCm`yDaRf!>c=hTp`CGLDz;LF>Q}>)96VcLiLcMbi=Ug=u zkqR)`q8Z2lpTFx%93J=#*&v7U5qom`sC?nt=DWU2w9+_nWOSCtN00E^g$um3_$C0# zw{r|s!jw{#$jD^pBcIY~o`2})3};7L-vjC0cLd!m`EVDab(|7zC3ocYoQ-rI?2KQaZ3BUBR-pqf2DKWE6)A#Y2Lnl3GYB# z=9-|eO?$gm*RwcPCTK^!V+Nc;iGoO@>LI=!UqoroTy`v)=``I^OTAe?W7k6Jj&jB+ zR3LytET}*bps~p(n}9Ypbvr~F zu`yWkq*iTB%Dtax)7vTAHuM1D?#8@c3&*6cPvcaTNJFHd5kOKj)HSjmt55oGC93My zIZd6h4+Fhp#G55r$V{n_Nnk>0CLlR_o~l?X-bqD8T+G}PeJEI`{14Mv1)uXXsb~NI N002ovPDHLkV1iTJGm-!R literal 0 HcmV?d00001 diff --git a/source/net/sourceforge/filebot/ui/AbstractSearchPanel.java b/source/net/sourceforge/filebot/ui/AbstractSearchPanel.java index 81886c0a..52f05b62 100644 --- a/source/net/sourceforge/filebot/ui/AbstractSearchPanel.java +++ b/source/net/sourceforge/filebot/ui/AbstractSearchPanel.java @@ -75,8 +75,8 @@ public abstract class AbstractSearchPanel extends JComponent { searchTextField.getEditor().setAction(searchAction); - searchTextField.getSelectButton().setModel(Arrays.asList(createSearchEngines())); - searchTextField.getSelectButton().setLabelProvider(createSearchEngineLabelProvider()); + searchTextField.getSelectButton().setModel(Arrays.asList(getSearchEngines())); + searchTextField.getSelectButton().setLabelProvider(getSearchEngineLabelProvider()); try { // restore selected subtitle client @@ -101,10 +101,10 @@ public abstract class AbstractSearchPanel extends JComponent { } - protected abstract S[] createSearchEngines(); + protected abstract S[] getSearchEngines(); - protected abstract LabelProvider createSearchEngineLabelProvider(); + protected abstract LabelProvider getSearchEngineLabelProvider(); protected abstract Settings getSettings(); diff --git a/source/net/sourceforge/filebot/ui/panel/episodelist/EpisodeListPanel.java b/source/net/sourceforge/filebot/ui/panel/episodelist/EpisodeListPanel.java index 465f4abe..0d6089a2 100644 --- a/source/net/sourceforge/filebot/ui/panel/episodelist/EpisodeListPanel.java +++ b/source/net/sourceforge/filebot/ui/panel/episodelist/EpisodeListPanel.java @@ -79,7 +79,7 @@ public class EpisodeListPanel extends AbstractSearchPanel createSearchEngineLabelProvider() { + protected LabelProvider getSearchEngineLabelProvider() { return SimpleLabelProvider.forClass(EpisodeListProvider.class); } diff --git a/source/net/sourceforge/filebot/ui/panel/rename/HistoryDialog.java b/source/net/sourceforge/filebot/ui/panel/rename/HistoryDialog.java index 61eb2081..1cd3b138 100644 --- a/source/net/sourceforge/filebot/ui/panel/rename/HistoryDialog.java +++ b/source/net/sourceforge/filebot/ui/panel/rename/HistoryDialog.java @@ -449,7 +449,7 @@ class HistoryDialog extends JDialog { type = QUESTION_MESSAGE; options = EnumSet.of(Option.Rename, Option.ChangeDirectory, Option.Cancel); } else { - String text = String.format("Some files are missing. Please select a different directory."); + String text = "Some files are missing. Please select a different directory."; JList missingFilesComponent = new JList(missingFiles.toArray()) { @Override diff --git a/source/net/sourceforge/filebot/ui/panel/subtitle/SimpleComboBox.java b/source/net/sourceforge/filebot/ui/panel/subtitle/SimpleComboBox.java new file mode 100644 index 00000000..170dc354 --- /dev/null +++ b/source/net/sourceforge/filebot/ui/panel/subtitle/SimpleComboBox.java @@ -0,0 +1,70 @@ + +package net.sourceforge.filebot.ui.panel.subtitle; + + +import static javax.swing.BorderFactory.*; + +import java.awt.Color; +import java.awt.Rectangle; + +import javax.swing.JButton; +import javax.swing.JComboBox; +import javax.swing.plaf.basic.BasicComboBoxUI; +import javax.swing.plaf.basic.BasicComboPopup; +import javax.swing.plaf.basic.ComboPopup; + +import net.sourceforge.filebot.ResourceManager; + + +public class SimpleComboBox extends JComboBox { + + public SimpleComboBox() { + setUI(new SimpleComboBoxUI()); + setBorder(createEmptyBorder()); + } + + + private static class SimpleComboBoxUI extends BasicComboBoxUI { + + @Override + protected JButton createArrowButton() { + JButton button = new JButton(ResourceManager.getIcon("arrow.down")); + button.setContentAreaFilled(false); + button.setBorderPainted(false); + button.setFocusPainted(false); + button.setOpaque(false); + + return button; + } + + + @Override + protected ComboPopup createPopup() { + return new BasicComboPopup(comboBox) { + + @Override + protected Rectangle computePopupBounds(int px, int py, int pw, int ph) { + Rectangle bounds = super.computePopupBounds(px, py, pw, ph); + + // allow combobox popup to be wider than the combobox itself + bounds.width = Math.max(bounds.width, list.getPreferredSize().width); + + return bounds; + } + + + @Override + protected void configurePopup() { + super.configurePopup(); + + setOpaque(true); + setBackground(list.getBackground()); + + // use gray instead of black border for combobox popup + setBorder(createCompoundBorder(createLineBorder(Color.gray, 1), createEmptyBorder(1, 1, 1, 1))); + } + }; + } + } + +} diff --git a/source/net/sourceforge/filebot/ui/panel/subtitle/SubtitleDropTarget.java b/source/net/sourceforge/filebot/ui/panel/subtitle/SubtitleDropTarget.java new file mode 100644 index 00000000..26205df9 --- /dev/null +++ b/source/net/sourceforge/filebot/ui/panel/subtitle/SubtitleDropTarget.java @@ -0,0 +1,238 @@ + +package net.sourceforge.filebot.ui.panel.subtitle; + + +import static net.sourceforge.filebot.MediaTypes.*; +import static net.sourceforge.filebot.ui.transfer.FileTransferable.*; +import static net.sourceforge.tuned.FileUtilities.*; +import static net.sourceforge.tuned.ui.TunedUtilities.*; + +import java.awt.Color; +import java.awt.Cursor; +import java.awt.dnd.DnDConstants; +import java.awt.dnd.DropTarget; +import java.awt.dnd.DropTargetAdapter; +import java.awt.dnd.DropTargetDragEvent; +import java.awt.dnd.DropTargetDropEvent; +import java.awt.dnd.DropTargetEvent; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.File; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.swing.Icon; +import javax.swing.JButton; +import javax.swing.JDialog; +import javax.swing.JFileChooser; +import javax.swing.filechooser.FileNameExtensionFilter; + +import net.sourceforge.filebot.ResourceManager; +import net.sourceforge.filebot.web.VideoHashSubtitleService; + + +abstract class SubtitleDropTarget extends JButton { + + private enum DropAction { + Download, + Upload, + Cancel + } + + + public SubtitleDropTarget() { + setHorizontalAlignment(CENTER); + + setHideActionText(true); + setBorderPainted(false); + setContentAreaFilled(false); + setFocusPainted(false); + + setBackground(Color.white); + setOpaque(false); + + // initialize with default mode + setDropAction(DropAction.Download); + setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); + + // install mouse listener + addActionListener(clickHandler); + + // install drop target + new DropTarget(this, dropHandler); + } + + + private void setDropAction(DropAction dropAction) { + setIcon(getIcon(dropAction)); + } + + + private Icon getIcon(DropAction dropAction) { + switch (dropAction) { + case Download: + return ResourceManager.getIcon("subtitle.exact.download"); + case Upload: + return ResourceManager.getIcon("subtitle.exact.upload"); + default: + return ResourceManager.getIcon("message.error"); + } + } + + + public abstract VideoHashSubtitleService[] getServices(); + + + public abstract String getQueryLanguage(); + + + private boolean handleDownload(List videoFiles) { + VideoHashSubtitleDownloadDialog dialog = new VideoHashSubtitleDownloadDialog(getWindow(this)); + + // initialize download parameters + dialog.setVideoFiles(videoFiles.toArray(new File[0])); + + for (VideoHashSubtitleService service : getServices()) { + dialog.addSubtitleService(service); + } + + // start looking for subtitles + dialog.startQuery(getQueryLanguage()); + + // initialize window properties + dialog.setIconImage(getImage(getIcon(DropAction.Download))); + dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); + dialog.pack(); + + // show dialog + dialog.setLocation(getOffsetLocation(dialog.getOwner())); + dialog.setVisible(true); + + // now it's up to the user + return true; + } + + + private boolean handleUpload(Map videosMappedBySubtitle) { + // TODO implement upload + throw new UnsupportedOperationException("Not implemented yet"); + } + + + private boolean handleDrop(List files) { + // perform a drop action depending on the given files + if (containsOnly(files, VIDEO_FILES)) { + return handleDownload(files); + } + + if (containsOnly(files, FOLDERS)) { + // collect all video files from the dropped folders + return handleDownload(filter(listFiles(files, 0), VIDEO_FILES)); + } + + if (containsOnly(files, SUBTITLE_FILES)) { + // TODO implement upload + throw new UnsupportedOperationException("Not implemented yet"); + } + + if (filter(files, VIDEO_FILES).size() == filter(files, SUBTITLE_FILES).size()) { + // TODO implement upload + throw new UnsupportedOperationException("Not implemented yet"); + } + + return false; + } + + + private DropAction getDropAction(List files) { + // video files only, or any folder, containing video files + if (containsOnly(files, VIDEO_FILES) || (containsOnly(files, FOLDERS) && filter(listFiles(files, 0), VIDEO_FILES).size() > 0)) { + return DropAction.Download; + } + + // subtitle files only, or video/subtitle pairs + if (containsOnly(files, SUBTITLE_FILES) || filter(files, VIDEO_FILES).size() == filter(files, SUBTITLE_FILES).size()) { + return DropAction.Upload; + } + + // unknown input + return DropAction.Cancel; + } + + + private final DropTargetAdapter dropHandler = new DropTargetAdapter() { + + @Override + public void dragEnter(DropTargetDragEvent dtde) { + DropAction dropAction = DropAction.Cancel; + + try { + dropAction = getDropAction(getFilesFromTransferable(dtde.getTransferable())); + } catch (Exception e) { + // ignore + } + + // update visual representation + setDropAction(dropAction); + + // accept or reject + if (dropAction != DropAction.Cancel) { + dtde.acceptDrag(DnDConstants.ACTION_COPY); + } else { + dtde.rejectDrag(); + } + } + + + public void dragExit(DropTargetEvent dte) { + // reset to default state + setDropAction(DropAction.Download); + }; + + + @Override + public void drop(DropTargetDropEvent dtde) { + dtde.acceptDrop(DnDConstants.ACTION_REFERENCE); + + try { + dtde.dropComplete(handleDrop(getFilesFromTransferable(dtde.getTransferable()))); + } catch (Exception e) { + Logger.getLogger("ui").log(Level.WARNING, e.getMessage(), e); + } + + // reset to default state + dragExit(dtde); + } + + }; + + private final ActionListener clickHandler = new ActionListener() { + + @Override + public void actionPerformed(ActionEvent evt) { + JFileChooser chooser = new JFileChooser(); + chooser.setMultiSelectionEnabled(true); + + // collect media file extensions (video and subtitle files) + List extensions = new ArrayList(); + Collections.addAll(extensions, VIDEO_FILES.extensions()); + Collections.addAll(extensions, SUBTITLE_FILES.extensions()); + + chooser.setFileFilter(new FileNameExtensionFilter("Media files", extensions.toArray(new String[0]))); + + if (chooser.showOpenDialog(getWindow(evt.getSource())) == JFileChooser.APPROVE_OPTION) { + List files = Arrays.asList(chooser.getSelectedFiles()); + + if (getDropAction(files) != DropAction.Cancel) { + handleDrop(Arrays.asList(chooser.getSelectedFiles())); + } + } + } + }; + +} diff --git a/source/net/sourceforge/filebot/ui/panel/subtitle/SubtitlePanel.java b/source/net/sourceforge/filebot/ui/panel/subtitle/SubtitlePanel.java index 6066ba51..a9511fe6 100644 --- a/source/net/sourceforge/filebot/ui/panel/subtitle/SubtitlePanel.java +++ b/source/net/sourceforge/filebot/ui/panel/subtitle/SubtitlePanel.java @@ -2,10 +2,14 @@ package net.sourceforge.filebot.ui.panel.subtitle; -import static net.sourceforge.filebot.Settings.*; import static net.sourceforge.filebot.ui.panel.subtitle.LanguageComboBoxModel.*; +import java.awt.Color; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.RenderingHints; import java.awt.event.ItemEvent; +import java.awt.geom.Path2D; import java.net.URI; import java.util.AbstractList; import java.util.ArrayList; @@ -19,13 +23,10 @@ import javax.swing.JComboBox; import net.sourceforge.filebot.Settings; import net.sourceforge.filebot.ui.AbstractSearchPanel; import net.sourceforge.filebot.ui.SelectDialog; -import net.sourceforge.filebot.web.OpenSubtitlesClient; import net.sourceforge.filebot.web.SearchResult; -import net.sourceforge.filebot.web.SublightSubtitleClient; -import net.sourceforge.filebot.web.SubsceneSubtitleClient; import net.sourceforge.filebot.web.SubtitleDescriptor; import net.sourceforge.filebot.web.SubtitleProvider; -import net.sourceforge.filebot.web.SubtitleSourceClient; +import net.sourceforge.filebot.web.VideoHashSubtitleService; import net.sourceforge.tuned.PreferencesList; import net.sourceforge.tuned.PreferencesMap.PreferencesEntry; import net.sourceforge.tuned.ui.LabelProvider; @@ -92,22 +93,60 @@ public class SubtitlePanel extends AbstractSearchPanel createSearchEngineLabelProvider() { + protected LabelProvider getSearchEngineLabelProvider() { return SimpleLabelProvider.forClass(SubtitleProvider.class); } diff --git a/source/net/sourceforge/filebot/ui/panel/subtitle/SubtitleServices.java b/source/net/sourceforge/filebot/ui/panel/subtitle/SubtitleServices.java new file mode 100644 index 00000000..5ca7ca1f --- /dev/null +++ b/source/net/sourceforge/filebot/ui/panel/subtitle/SubtitleServices.java @@ -0,0 +1,41 @@ + +package net.sourceforge.filebot.ui.panel.subtitle; + + +import static net.sourceforge.filebot.Settings.*; + +import net.sourceforge.filebot.web.OpenSubtitlesClient; +import net.sourceforge.filebot.web.SublightSubtitleClient; +import net.sourceforge.filebot.web.SubsceneSubtitleClient; +import net.sourceforge.filebot.web.SubtitleProvider; +import net.sourceforge.filebot.web.SubtitleSourceClient; +import net.sourceforge.filebot.web.VideoHashSubtitleService; + + +final class SubtitleServices { + + public static final OpenSubtitlesClient openSubtitlesClient = new OpenSubtitlesClient(String.format("%s %s", getApplicationName(), getApplicationVersion())); + public static final SublightSubtitleClient sublightSubtitleClient = new SublightSubtitleClient(getApplicationName(), getApplicationProperty("sublight.apikey")); + + public static final SubsceneSubtitleClient subsceneSubtitleClient = new SubsceneSubtitleClient(); + public static final SubtitleSourceClient subtitleSourceClient = new SubtitleSourceClient(); + + + public static SubtitleProvider[] getSubtitleProviders() { + return new SubtitleProvider[] { openSubtitlesClient, subsceneSubtitleClient, sublightSubtitleClient, subtitleSourceClient }; + } + + + public static VideoHashSubtitleService[] getVideoHashSubtitleServices() { + return new VideoHashSubtitleService[] { openSubtitlesClient, sublightSubtitleClient }; + } + + + /** + * Dummy constructor to prevent instantiation. + */ + private SubtitleServices() { + throw new UnsupportedOperationException(); + } + +} diff --git a/source/net/sourceforge/filebot/ui/panel/subtitle/VideoHashSubtitleDownloadDialog.java b/source/net/sourceforge/filebot/ui/panel/subtitle/VideoHashSubtitleDownloadDialog.java new file mode 100644 index 00000000..73c78a65 --- /dev/null +++ b/source/net/sourceforge/filebot/ui/panel/subtitle/VideoHashSubtitleDownloadDialog.java @@ -0,0 +1,804 @@ + +package net.sourceforge.filebot.ui.panel.subtitle; + + +import static javax.swing.BorderFactory.*; +import static javax.swing.JOptionPane.*; +import static net.sourceforge.filebot.ui.panel.subtitle.SubtitleUtilities.*; + +import java.awt.Color; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.Window; +import java.awt.event.ActionEvent; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.io.File; +import java.net.URI; +import java.nio.ByteBuffer; +import java.util.AbstractList; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.swing.AbstractAction; +import javax.swing.Action; +import javax.swing.DefaultCellEditor; +import javax.swing.DefaultComboBoxModel; +import javax.swing.DefaultListCellRenderer; +import javax.swing.DefaultListSelectionModel; +import javax.swing.Icon; +import javax.swing.JButton; +import javax.swing.JComboBox; +import javax.swing.JComponent; +import javax.swing.JDialog; +import javax.swing.JList; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTable; +import javax.swing.SwingWorker; +import javax.swing.SwingWorker.StateValue; +import javax.swing.border.Border; +import javax.swing.table.AbstractTableModel; +import javax.swing.table.DefaultTableCellRenderer; + +import net.miginfocom.swing.MigLayout; +import net.sourceforge.filebot.ResourceManager; +import net.sourceforge.filebot.web.SubtitleDescriptor; +import net.sourceforge.filebot.web.VideoHashSubtitleService; +import net.sourceforge.tuned.FileUtilities; +import net.sourceforge.tuned.ui.AbstractBean; +import net.sourceforge.tuned.ui.LinkButton; +import net.sourceforge.tuned.ui.RoundBorder; + + +class VideoHashSubtitleDownloadDialog extends JDialog { + + private final JPanel servicePanel = new JPanel(new MigLayout()); + private final List services = new ArrayList(); + + private final JTable subtitleMappingTable = createTable(); + + private ExecutorService downloadService; + + + public VideoHashSubtitleDownloadDialog(Window owner) { + super(owner, "Download Subtitles", ModalityType.MODELESS); + + JComponent content = (JComponent) getContentPane(); + content.setLayout(new MigLayout("fill, insets dialog, nogrid", "", "[fill][pref!]")); + + servicePanel.setBorder(new RoundBorder()); + servicePanel.setOpaque(false); + servicePanel.setBackground(new Color(0xFAFAD2)); // LightGoldenRodYellow + + content.add(new JScrollPane(subtitleMappingTable), "grow, wrap"); + content.add(servicePanel, "gap after indent*2"); + + content.add(new JButton(downloadAction), "tag ok"); + content.add(new JButton(finishAction), "tag cancel"); + } + + + protected JTable createTable() { + JTable table = new JTable(new SubtitleMappingTableModel()); + table.setDefaultRenderer(SubtitleMapping.class, new SubtitleMappingOptionRenderer()); + + table.setRowHeight(24); + table.setIntercellSpacing(new Dimension(5, 5)); + + table.setBackground(Color.white); + table.setAutoCreateRowSorter(true); + table.setFillsViewportHeight(true); + + JComboBox editor = new SimpleComboBox(); + editor.setRenderer(new SubtitleOptionRenderer()); + editor.setFocusable(false); + + table.setDefaultEditor(SubtitleMapping.class, new DefaultCellEditor(editor) { + + @Override + public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) { + JComboBox editor = (JComboBox) super.getTableCellEditorComponent(table, value, isSelected, row, column); + + SubtitleMapping mapping = (SubtitleMapping) value; + editor.setModel(new DefaultComboBoxModel(mapping.getOptions())); + editor.setSelectedItem(mapping.getSelectedOption()); + + return editor; + } + }); + + // disable selection + table.setSelectionModel(new DefaultListSelectionModel() { + + @Override + public void addSelectionInterval(int from, int to) { + // ignore + } + + + @Override + public void setSelectionInterval(int from, int to) { + // ignore + } + + + @Override + public void setAnchorSelectionIndex(int anchorIndex) { + // ignore + } + + + @Override + public void setLeadSelectionIndex(int leadIndex) { + // ignore + } + }); + + return table; + } + + + public void setVideoFiles(File[] videoFiles) { + subtitleMappingTable.setModel(new SubtitleMappingTableModel(videoFiles)); + } + + + public void addSubtitleService(final VideoHashSubtitleService service) { + final VideoHashSubtitleServiceBean serviceBean = new VideoHashSubtitleServiceBean(service); + final LinkButton component = new LinkButton(serviceBean.getName(), ResourceManager.getIcon("database.go"), serviceBean.getLink()); + + serviceBean.addPropertyChangeListener(new PropertyChangeListener() { + + @Override + public void propertyChange(PropertyChangeEvent evt) { + if (serviceBean.getState() == StateValue.STARTED) { + component.setIcon(ResourceManager.getIcon("database.go")); + } else { + component.setIcon(ResourceManager.getIcon(serviceBean.getError() == null ? "database.ok" : "database.error")); + } + + component.setToolTipText(serviceBean.getError() == null ? null : serviceBean.getError().getMessage()); + } + }); + + services.add(serviceBean); + servicePanel.add(component); + } + + + public void startQuery(String languageName) { + final SubtitleMappingTableModel mappingModel = (SubtitleMappingTableModel) subtitleMappingTable.getModel(); + + // query services concurrently + for (VideoHashSubtitleServiceBean service : services) { + QueryTask task = new QueryTask(service, mappingModel.getVideoFiles(), languageName) { + + @Override + protected void done() { + try { + Map> subtitles = get(); + + // update subtitle options + for (SubtitleMapping subtitleMapping : mappingModel) { + List options = subtitles.get(subtitleMapping.getVideoFile()); + + if (options != null && options.size() > 0) { + subtitleMapping.addOptions(options); + } + } + + // make subtitle column visible + mappingModel.setOptionColumnVisible(true); + } catch (Exception e) { + Logger.getLogger(getClass().getName()).log(Level.WARNING, e.getMessage(), e); + } + } + }; + + // start background worker + task.execute(); + } + } + + + private Boolean showConfirmReplaceDialog(List files) { + JList existingFilesComponent = new JList(files.toArray()) { + + @Override + public Dimension getPreferredScrollableViewportSize() { + // adjust component size + return new Dimension(80, 50); + } + }; + + Object[] message = new Object[] { "Replace existing subtitle files?", new JScrollPane(existingFilesComponent) }; + Object[] options = new Object[] { "Replace All", "Skip All", "Cancel" }; + JOptionPane optionPane = new JOptionPane(message, WARNING_MESSAGE, YES_NO_CANCEL_OPTION, null, options); + + // display option dialog + optionPane.createDialog(VideoHashSubtitleDownloadDialog.this, "Replace").setVisible(true); + + // replace all + if (options[0] == optionPane.getValue()) + return true; + + // skip all + if (options[1] == optionPane.getValue()) + return false; + + // cancel + return null; + } + + + private final Action downloadAction = new AbstractAction("Download", ResourceManager.getIcon("dialog.continue")) { + + @Override + public void actionPerformed(ActionEvent evt) { + // don't allow restart of download as long as there are still unfinished download tasks + if (downloadService != null && !downloadService.isTerminated()) { + return; + } + + final SubtitleMappingTableModel mappingModel = (SubtitleMappingTableModel) subtitleMappingTable.getModel(); + + // collect the subtitles that will be fetched + List downloadQueue = new ArrayList(); + + for (SubtitleMapping mapping : mappingModel) { + SubtitleDescriptorBean subtitleBean = mapping.getSelectedOption(); + + if (subtitleBean != null && subtitleBean.getState() == null) { + downloadQueue.add(new DownloadTask(subtitleBean, mapping.getSubtitleFile())); + } + } + + // collect downloads that will override a file + List confirmReplaceDownloadQueue = new ArrayList(); + List existingFiles = new ArrayList(); + + for (DownloadTask download : downloadQueue) { + if (download.getDestination().exists()) { + confirmReplaceDownloadQueue.add(download); + existingFiles.add(download.getDestination().getName()); + } + } + + // confirm replace + if (confirmReplaceDownloadQueue.size() > 0) { + Boolean option = showConfirmReplaceDialog(existingFiles); + + // abort the operation altogether + if (option == null) { + return; + } + + // don't replace any files + if (option == false) { + downloadQueue.removeAll(confirmReplaceDownloadQueue); + } + } + + // start download + if (downloadQueue.size() > 0) { + downloadService = Executors.newSingleThreadExecutor(); + + for (DownloadTask downloadTask : downloadQueue) { + downloadTask.getSubtitleBean().setState(StateValue.PENDING); + downloadService.execute(downloadTask); + } + + // terminate after all downloads have been completed + downloadService.shutdown(); + } + } + }; + + private final Action finishAction = new AbstractAction("Close", ResourceManager.getIcon("dialog.cancel")) { + + @Override + public void actionPerformed(ActionEvent evt) { + if (downloadService != null) { + downloadService.shutdownNow(); + } + + setVisible(false); + dispose(); + } + }; + + + private static class SubtitleMappingOptionRenderer extends DefaultTableCellRenderer { + + private final JComboBox optionComboBox = new SimpleComboBox(); + + + public SubtitleMappingOptionRenderer() { + optionComboBox.setRenderer(new SubtitleOptionRenderer()); + } + + + @Override + public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { + SubtitleMapping mapping = (SubtitleMapping) value; + SubtitleDescriptorBean subtitleBean = mapping.getSelectedOption(); + + // render combobox for subtitle options + if (mapping.isEditable()) { + optionComboBox.setModel(new DefaultComboBoxModel(new Object[] { subtitleBean })); + return optionComboBox; + } + + // render status label + super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); + setForeground(table.getForeground()); + + if (subtitleBean == null) { + // no subtitles found + setText("No subtitles found"); + setIcon(null); + setForeground(Color.gray); + } else if (subtitleBean.getState() == StateValue.PENDING) { + // download in the queue + setText(subtitleBean.getText()); + setIcon(ResourceManager.getIcon("worker.pending")); + } else if (subtitleBean.getState() == StateValue.STARTED) { + // download in progress + setText(subtitleBean.getText()); + setIcon(ResourceManager.getIcon("action.fetch")); + } else { + // download complete + setText(mapping.getSubtitleFile().getName()); + setIcon(ResourceManager.getIcon("status.ok")); + } + + return this; + } + } + + + private static class SubtitleOptionRenderer extends DefaultListCellRenderer { + + private final Border padding = createEmptyBorder(3, 3, 3, 3); + + + @Override + public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { + super.getListCellRendererComponent(list, null, index, isSelected, cellHasFocus); + setBorder(padding); + + SubtitleDescriptorBean subtitleBean = (SubtitleDescriptorBean) value; + setText(subtitleBean.getText()); + setIcon(subtitleBean.getError() == null ? subtitleBean.getIcon() : ResourceManager.getIcon("status.warning")); + + return this; + } + } + + + private static class SubtitleMappingTableModel extends AbstractTableModel implements Iterable { + + private final SubtitleMapping[] data; + + private boolean optionColumnVisible = false; + + + public SubtitleMappingTableModel(File... videoFiles) { + data = new SubtitleMapping[videoFiles.length]; + + for (int i = 0; i < videoFiles.length; i++) { + data[i] = new SubtitleMapping(videoFiles[i]); + data[i].addPropertyChangeListener(new SubtitleMappingListener(i)); + } + } + + + public List getVideoFiles() { + return new AbstractList() { + + @Override + public File get(int index) { + return data[index].getVideoFile(); + } + + + @Override + public int size() { + return data.length; + } + }; + } + + + @Override + public Iterator iterator() { + return Arrays.asList(data).iterator(); + } + + + public void setOptionColumnVisible(boolean optionColumnVisible) { + this.optionColumnVisible = optionColumnVisible; + + // update columns + fireTableStructureChanged(); + } + + + @Override + public int getColumnCount() { + return optionColumnVisible ? 2 : 1; + } + + + @Override + public String getColumnName(int column) { + switch (column) { + case 0: + return "Video"; + case 1: + return "Subtitle"; + } + + return null; + } + + + @Override + public int getRowCount() { + return data.length; + } + + + @Override + public Object getValueAt(int row, int column) { + switch (column) { + case 0: + return data[row].getVideoFile().getName(); + case 1: + return data[row]; + } + + return null; + } + + + @Override + public void setValueAt(Object value, int row, int column) { + data[row].setSelectedOption((SubtitleDescriptorBean) value); + } + + + @Override + public boolean isCellEditable(int row, int column) { + return column == 1 && data[row].isEditable(); + } + + + @Override + public Class getColumnClass(int column) { + switch (column) { + case 0: + return String.class; + case 1: + return SubtitleMapping.class; + } + + return null; + } + + + private class SubtitleMappingListener implements PropertyChangeListener { + + private final int index; + + + public SubtitleMappingListener(int index) { + this.index = index; + } + + + @Override + public void propertyChange(PropertyChangeEvent evt) { + // update state and subtitle options + if (optionColumnVisible) { + fireTableCellUpdated(index, 1); + } + } + } + } + + + private static class SubtitleMapping extends AbstractBean { + + private final File videoFile; + + private SubtitleDescriptorBean selectedOption; + private List options = new ArrayList(); + + + public SubtitleMapping(File videoFile) { + this.videoFile = videoFile; + } + + + public File getVideoFile() { + return videoFile; + } + + + public File getSubtitleFile() { + return new File(videoFile.getParentFile(), FileUtilities.getName(videoFile) + '.' + selectedOption.getType()); + } + + + public boolean isEditable() { + return selectedOption != null && (selectedOption.getState() == null || selectedOption.getError() != null); + } + + + public SubtitleDescriptorBean getSelectedOption() { + return selectedOption; + } + + + public void setSelectedOption(SubtitleDescriptorBean selectedOption) { + if (this.selectedOption != null) { + this.selectedOption.removePropertyChangeListener(selectedOptionListener); + } + + this.selectedOption = selectedOption; + this.selectedOption.addPropertyChangeListener(selectedOptionListener); + + firePropertyChange("selectedOption", null, this.selectedOption); + } + + + public SubtitleDescriptorBean[] getOptions() { + return options.toArray(new SubtitleDescriptorBean[0]); + } + + + public void addOptions(List options) { + this.options.addAll(options); + + if (selectedOption == null && options.size() > 0) { + setSelectedOption(options.get(0)); + } + } + + + private final PropertyChangeListener selectedOptionListener = new PropertyChangeListener() { + + @Override + public void propertyChange(PropertyChangeEvent evt) { + firePropertyChange("selectedOption", null, selectedOption); + } + }; + } + + + private static class SubtitleDescriptorBean extends AbstractBean { + + private final SubtitleDescriptor subtitle; + private final VideoHashSubtitleServiceBean source; + + private StateValue state; + private Exception error; + + + public SubtitleDescriptorBean(SubtitleDescriptor subtitle, VideoHashSubtitleServiceBean source) { + this.subtitle = subtitle; + this.source = source; + } + + + public String getText() { + return subtitle.getName() + '.' + subtitle.getType(); + } + + + public Icon getIcon() { + return source.getIcon(); + } + + + public String getType() { + return subtitle.getType(); + } + + + public ByteBuffer fetch() throws Exception { + setState(StateValue.STARTED); + + try { + return subtitle.fetch(); + } catch (Exception e) { + // remember exception + error = e; + + // rethrow exception + throw e; + } finally { + setState(StateValue.DONE); + } + } + + + public Exception getError() { + return error; + } + + + public StateValue getState() { + return state; + } + + + public void setState(StateValue state) { + this.state = state; + firePropertyChange("state", null, this.state); + } + + + @Override + public String toString() { + return getText(); + } + } + + + private static class QueryTask extends SwingWorker>, Void> { + + private final VideoHashSubtitleServiceBean service; + + private final File[] videoFiles; + private final String languageName; + + + public QueryTask(VideoHashSubtitleServiceBean service, Collection videoFiles, String languageName) { + this.service = service; + this.videoFiles = videoFiles.toArray(new File[0]); + this.languageName = languageName; + } + + + @Override + protected Map> doInBackground() throws Exception { + Map> subtitleSet = new HashMap>(); + + for (final Entry> result : service.getSubtitleList(videoFiles, languageName).entrySet()) { + List subtitles = new ArrayList(); + + // associate subtitles with services + for (SubtitleDescriptor subtitleDescriptor : result.getValue()) { + subtitles.add(new SubtitleDescriptorBean(subtitleDescriptor, service)); + } + + subtitleSet.put(result.getKey(), subtitles); + } + + return subtitleSet; + } + } + + + private static class DownloadTask extends SwingWorker { + + private final SubtitleDescriptorBean subtitle; + private final File destination; + + + public DownloadTask(SubtitleDescriptorBean subtitle, File destination) { + this.subtitle = subtitle; + this.destination = destination; + } + + + public SubtitleDescriptorBean getSubtitleBean() { + return subtitle; + } + + + public File getDestination() { + return destination; + } + + + @Override + protected File doInBackground() { + try { + // fetch subtitle + ByteBuffer data = subtitle.fetch(); + + if (isCancelled()) + return null; + + // save to file + write(data, destination); + + return destination; + } catch (Exception e) { + Logger.getLogger("ui").log(Level.WARNING, e.getMessage(), e); + } + + return null; + } + } + + + private static class VideoHashSubtitleServiceBean extends AbstractBean { + + private final VideoHashSubtitleService service; + + private StateValue state; + private Throwable error; + + + public VideoHashSubtitleServiceBean(VideoHashSubtitleService service) { + this.service = service; + this.state = StateValue.PENDING; + } + + + public String getName() { + return service.getName(); + } + + + public Icon getIcon() { + return service.getIcon(); + } + + + public URI getLink() { + return service.getLink(); + } + + + public Map> getSubtitleList(File[] files, String languageName) throws Exception { + setState(StateValue.STARTED); + + try { + return service.getSubtitleList(files, languageName); + } catch (Exception e) { + // remember error + error = e; + + // rethrow error + throw e; + } finally { + setState(StateValue.DONE); + } + } + + + private void setState(StateValue state) { + this.state = state; + firePropertyChange("state", null, this.state); + } + + + public StateValue getState() { + return state; + } + + + public Throwable getError() { + return error; + } + + } + +} diff --git a/source/net/sourceforge/filebot/web/OpenSubtitlesClient.java b/source/net/sourceforge/filebot/web/OpenSubtitlesClient.java index 9e95a8a0..28070dd8 100644 --- a/source/net/sourceforge/filebot/web/OpenSubtitlesClient.java +++ b/source/net/sourceforge/filebot/web/OpenSubtitlesClient.java @@ -44,6 +44,12 @@ public class OpenSubtitlesClient implements SubtitleProvider, VideoHashSubtitleS } + @Override + public URI getLink() { + return URI.create("http://www.opensubtitles.org"); + } + + @Override public Icon getIcon() { return ResourceManager.getIcon("search.opensubtitles"); diff --git a/source/net/sourceforge/filebot/web/SublightSubtitleClient.java b/source/net/sourceforge/filebot/web/SublightSubtitleClient.java index 3140c599..5a5ddd0b 100644 --- a/source/net/sourceforge/filebot/web/SublightSubtitleClient.java +++ b/source/net/sourceforge/filebot/web/SublightSubtitleClient.java @@ -58,6 +58,12 @@ public class SublightSubtitleClient implements SubtitleProvider, VideoHashSubtit } + @Override + public URI getLink() { + return URI.create("http://www.sublight.si"); + } + + @Override public Icon getIcon() { return ResourceManager.getIcon("search.sublight"); @@ -131,7 +137,7 @@ public class SublightSubtitleClient implements SubtitleProvider, VideoHashSubtit } } catch (LinkageError e) { // MediaInfo native lib not available - throw new UnsupportedOperationException(e); + throw new UnsupportedOperationException(e.getMessage(), e); } return subtitles; @@ -292,7 +298,8 @@ public class SublightSubtitleClient implements SubtitleProvider, VideoHashSubtit @Override public URI getSubtitleListLink(SearchResult searchResult, String languageName) { - return null; + // note that sublight can only be accessed via the soap API + return URI.create("http://www.sublight.si/SearchSubtitles.aspx"); } diff --git a/source/net/sourceforge/filebot/web/SubsceneSubtitleClient.java b/source/net/sourceforge/filebot/web/SubsceneSubtitleClient.java index 3033adfc..e74bfecf 100644 --- a/source/net/sourceforge/filebot/web/SubsceneSubtitleClient.java +++ b/source/net/sourceforge/filebot/web/SubsceneSubtitleClient.java @@ -44,6 +44,12 @@ public class SubsceneSubtitleClient implements SubtitleProvider { } + @Override + public URI getLink() { + return URI.create("http://subscene.com"); + } + + @Override public Icon getIcon() { return ResourceManager.getIcon("search.subscene"); diff --git a/source/net/sourceforge/filebot/web/SubtitleProvider.java b/source/net/sourceforge/filebot/web/SubtitleProvider.java index 94ddf8bf..2a3b61dc 100644 --- a/source/net/sourceforge/filebot/web/SubtitleProvider.java +++ b/source/net/sourceforge/filebot/web/SubtitleProvider.java @@ -22,6 +22,9 @@ public interface SubtitleProvider { public String getName(); + public URI getLink(); + + public Icon getIcon(); } diff --git a/source/net/sourceforge/filebot/web/SubtitleSourceClient.java b/source/net/sourceforge/filebot/web/SubtitleSourceClient.java index f3134f71..9b6bf83d 100644 --- a/source/net/sourceforge/filebot/web/SubtitleSourceClient.java +++ b/source/net/sourceforge/filebot/web/SubtitleSourceClient.java @@ -15,11 +15,11 @@ import java.util.Map; import javax.swing.Icon; -import net.sourceforge.filebot.ResourceManager; - import org.w3c.dom.Document; import org.w3c.dom.Node; +import net.sourceforge.filebot.ResourceManager; + public class SubtitleSourceClient implements SubtitleProvider { @@ -34,6 +34,12 @@ public class SubtitleSourceClient implements SubtitleProvider { } + @Override + public URI getLink() { + return URI.create("http://www.subtitlesource.org"); + } + + @Override public Icon getIcon() { return ResourceManager.getIcon("search.subtitlesource"); diff --git a/source/net/sourceforge/filebot/web/VideoHashSubtitleService.java b/source/net/sourceforge/filebot/web/VideoHashSubtitleService.java index 98402cea..fa906b5f 100644 --- a/source/net/sourceforge/filebot/web/VideoHashSubtitleService.java +++ b/source/net/sourceforge/filebot/web/VideoHashSubtitleService.java @@ -3,9 +3,12 @@ package net.sourceforge.filebot.web; import java.io.File; +import java.net.URI; import java.util.List; import java.util.Map; +import javax.swing.Icon; + public interface VideoHashSubtitleService { @@ -14,4 +17,13 @@ public interface VideoHashSubtitleService { public boolean publishSubtitle(int imdbid, String languageName, File videoFile, File subtitleFile) throws Exception; + + public String getName(); + + + public URI getLink(); + + + public Icon getIcon(); + } diff --git a/source/net/sourceforge/tuned/FileUtilities.java b/source/net/sourceforge/tuned/FileUtilities.java index 4c2ec31f..dd7f83d8 100644 --- a/source/net/sourceforge/tuned/FileUtilities.java +++ b/source/net/sourceforge/tuned/FileUtilities.java @@ -140,6 +140,32 @@ public final class FileUtilities { } + public static List listFiles(Iterable folders, int maxDepth) { + List files = new ArrayList(); + + // collect files from directory tree + for (File folder : folders) { + listFiles(folder, 0, files, maxDepth); + } + + return files; + } + + + private static void listFiles(File folder, int depth, List files, int maxDepth) { + if (depth > maxDepth) + return; + + for (File file : folder.listFiles()) { + if (file.isDirectory()) { + listFiles(file, depth + 1, files, maxDepth); + } else { + files.add(file); + } + } + } + + /** * Invalid filename characters: \, /, :, *, ?, ", <, >, |, \r and \n */ diff --git a/source/net/sourceforge/tuned/ui/AbstractBean.java b/source/net/sourceforge/tuned/ui/AbstractBean.java new file mode 100644 index 00000000..04fc9c67 --- /dev/null +++ b/source/net/sourceforge/tuned/ui/AbstractBean.java @@ -0,0 +1,46 @@ + +package net.sourceforge.tuned.ui; + + +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.beans.PropertyChangeSupport; + +import javax.swing.event.SwingPropertyChangeSupport; + + +public abstract class AbstractBean { + + private final PropertyChangeSupport pcs; + + + public AbstractBean() { + pcs = new SwingPropertyChangeSupport(this, true); + } + + + protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) { + pcs.firePropertyChange(propertyName, oldValue, newValue); + } + + + protected void firePropertyChange(PropertyChangeEvent e) { + pcs.firePropertyChange(e); + } + + + public void addPropertyChangeListener(PropertyChangeListener listener) { + pcs.addPropertyChangeListener(listener); + } + + + public void removePropertyChangeListener(PropertyChangeListener listener) { + pcs.removePropertyChangeListener(listener); + } + + + public PropertyChangeListener[] getPropertyChangeListeners() { + return pcs.getPropertyChangeListeners(); + } + +} diff --git a/source/net/sourceforge/tuned/ui/RoundBorder.java b/source/net/sourceforge/tuned/ui/RoundBorder.java new file mode 100644 index 00000000..fd663a38 --- /dev/null +++ b/source/net/sourceforge/tuned/ui/RoundBorder.java @@ -0,0 +1,73 @@ + +package net.sourceforge.tuned.ui; + + +import java.awt.Color; +import java.awt.Component; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Insets; +import java.awt.RenderingHints; + +import javax.swing.border.AbstractBorder; + + +public class RoundBorder extends AbstractBorder { + + private final Color color; + private final Insets insets; + private final int arc; + + + public RoundBorder() { + this.color = new Color(0xACACAC); + this.arc = 12; + this.insets = new Insets(1, 1, 1, 1); + } + + + public RoundBorder(Color color, int arc, Insets insets) { + this.color = color; + this.arc = arc; + this.insets = insets; + } + + + @Override + public boolean isBorderOpaque() { + return false; + } + + + @Override + public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) { + Graphics2D g2d = (Graphics2D) g.create(); + g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + + g2d.setPaint(c.getBackground()); + g2d.fillRoundRect(x, y, width - 1, height - 1, arc, arc); + + g2d.setPaint(color); + g2d.drawRoundRect(x, y, width - 1, height - 1, arc, arc); + + g2d.dispose(); + } + + + @Override + public Insets getBorderInsets(Component c) { + return new Insets(insets.top, insets.left, insets.bottom, insets.right); + } + + + @Override + public Insets getBorderInsets(Component c, Insets insets) { + insets.top = this.insets.top; + insets.left = this.insets.left; + insets.bottom = this.insets.bottom; + insets.right = this.insets.right; + + return insets; + } + +}