• R/O
  • HTTP
  • SSH
  • HTTPS

Commit

Frequently used words (click to add to your profile)

javac++androidlinuxc#windowsobjective-ccocoa誰得qtpythonphprubygameguibathyscaphec計画中(planning stage)翻訳omegatframeworktwitterdomtestvb.netdirectxゲームエンジンbtronarduinopreviewer

タイニー番組ナビゲータ本体


Commit MetaInfo

Revisión339befc2961ed8da020b08a0e8b30a0ec9c68ab2 (tree)
Tiempo2022-05-08 18:48:13
AutorMasahiko Kimura <mkimura@u01....>
CommiterMasahiko Kimura

Log Message

Ver.1.13.6 (2022/5/8)
1. [新聞形式]予約バーのツールチップで該当の時間帯の予約内容を表示する
2. [新聞形式]放送局別1週おき表示に対応する(ヘッダーの右クリックメニューで選択)
3. [しょぼかる]過去日に遡って番組情報を取得できるようにする(最大7日)
 (env.txtでsyobocalPastDaysに日数を指定する)
4. [新聞形式][タイトル一覧][タイトルダイアログ]3.で取得したしょぼかるの番組情報を利用する

Cambiar Resumen

Diferencia incremental

--- a/TinyBannavi/src/tainavi/AbsPaperView.java
+++ b/TinyBannavi/src/tainavi/AbsPaperView.java
@@ -13,6 +13,8 @@ import java.awt.Graphics2D;
1313 import java.awt.Point;
1414 import java.awt.Rectangle;
1515 import java.awt.Robot;
16+import java.awt.event.ActionEvent;
17+import java.awt.event.ActionListener;
1618 import java.awt.event.ComponentAdapter;
1719 import java.awt.event.ComponentEvent;
1820 import java.awt.event.ComponentListener;
@@ -37,7 +39,9 @@ import java.util.GregorianCalendar;
3739 import javax.swing.JComponent;
3840 import javax.swing.JLabel;
3941 import javax.swing.JLayeredPane;
42+import javax.swing.JMenuItem;
4043 import javax.swing.JPanel;
44+import javax.swing.JPopupMenu;
4145 import javax.swing.JScrollPane;
4246 import javax.swing.JSplitPane;
4347 import javax.swing.JTree;
@@ -57,6 +61,7 @@ import javax.swing.tree.TreeNode;
5761 import javax.swing.tree.TreePath;
5862 import javax.swing.tree.TreeSelectionModel;
5963
64+import tainavi.TVProgram.ProgGenre;
6065 import tainavi.TVProgram.ProgType;
6166 import tainavi.TVProgramIterator.IterationType;
6267 import tainavi.VWMainWindow.MWinTab;
@@ -287,6 +292,9 @@ public abstract class AbsPaperView extends JPanel implements TickTimerListener,H
287292 // スクロール関係
288293 private int buttonPressed = 0;
289294
295+ // 放送局別表示の日数間隔
296+ private int byCenterModeDayInterval = 1;
297+
290298 /*
291299 * ページャーのEnable/Disableを更新する
292300 */
@@ -483,6 +491,7 @@ public abstract class AbsPaperView extends JPanel implements TickTimerListener,H
483491 */
484492 public void jumpToNow() {
485493 if ( nowNode != null ) {
494+ this.startDate = CommonUtils.getDate529(0, true);
486495 TreePath tp = new TreePath(nowNode.getPath());
487496 setSelectionPath(null);
488497 setSelectionPath(tp);
@@ -691,6 +700,7 @@ public abstract class AbsPaperView extends JPanel implements TickTimerListener,H
691700 case PASSED:
692701 PassedProgram passed = tvprograms.getPassed();
693702 if ( passed.loadAllCenters(value) ) {
703+ attachSyoboPassed(passed);
694704 redrawByDate(value, IterationType.PASSED);
695705 }
696706 else {
@@ -1506,6 +1516,7 @@ public abstract class AbsPaperView extends JPanel implements TickTimerListener,H
15061516 System.err.println(ERRID+"過去ログの取得に失敗しました: "+date);
15071517 return null;
15081518 }
1519+ attachSyoboPassed(passed);
15091520
15101521 // 指定した日付のノードがツリーになければ作成する
15111522 addPassedNodeIfNotExist(date);
@@ -1755,7 +1766,7 @@ public abstract class AbsPaperView extends JPanel implements TickTimerListener,H
17551766
17561767 String today = CommonUtils.getDate529(0, true);
17571768 // 開始日が当日以降であれば現在の表を作成する
1758- if (startDate.compareTo(today) >= 0){
1769+ if (this.byCenterModeDayInterval == 1 && startDate.compareTo(today) >= 0){
17591770 startDate = today;
17601771 redrawByCenterCurrent(center);
17611772 }
@@ -1849,14 +1860,42 @@ public abstract class AbsPaperView extends JPanel implements TickTimerListener,H
18491860 * 放送局別に過去ログから表を作成する
18501861 */
18511862 private void redrawByCenterPassed(String center){
1863+ // 開始日を調整する
1864+ String today = CommonUtils.getDate529(0, true);
1865+
1866+ GregorianCalendar ct = CommonUtils.getCalendar(today);
1867+ ct.add(GregorianCalendar.DAY_OF_MONTH, 7-(env.getDatePerPassedPage()-1)*this.byCenterModeDayInterval);
1868+ String dateMax = CommonUtils.getDate(ct);
1869+
1870+ while (startDate.compareTo(dateMax) > 0){
1871+ ct = CommonUtils.getCalendar(startDate);
1872+ ct.add(GregorianCalendar.DAY_OF_MONTH, -this.byCenterModeDayInterval);
1873+ startDate = CommonUtils.getDate(ct);
1874+ }
1875+
18521876 // 過去ログの取得
18531877 PassedProgram tvp = tvprograms.getPassed();
18541878
18551879 // 開始日以降で指定日数分の番組情報を取得する
1856- ProgList pl = tvp.loadByCenterDates(startDate, env.getDatePerPassedPage(), center);
1880+ ProgList pl = tvp.loadByCenterDates(startDate, env.getDatePerPassedPage(), this.byCenterModeDayInterval, center);
18571881 if (pl == null)
18581882 return;
18591883
1884+ attachSyoboPassedProgList(pl);
1885+
1886+ // 未来分の番組情報をマージする
1887+ GregorianCalendar c2 = CommonUtils.getCalendar(startDate);
1888+ for (int n=0; n<env.getDatePerPassedPage(); n++){
1889+ String date = CommonUtils.getDate(c2);
1890+ if (date.compareTo(today) >= 0){
1891+ ProgDateList pcl = getProgDateListByCenterAndDate(center, date);
1892+ if (pcl != null)
1893+ pl.pdate.add(pcl);
1894+ }
1895+
1896+ c2.add(Calendar.DAY_OF_MONTH, this.byCenterModeDayInterval);
1897+ }
1898+
18601899 // 日付ヘッダを描画する
18611900 for (int centerid=0; centerid<pl.pdate.size(); centerid++) {
18621901 ProgDateList pcl = pl.pdate.get(centerid);
@@ -1898,6 +1937,53 @@ public abstract class AbsPaperView extends JPanel implements TickTimerListener,H
18981937 }
18991938
19001939 /**
1940+ * 指定された放送局の番組一覧を取得する
1941+ *
1942+ * @param center 対象の放送局
1943+ * @return 該当の番組一覧
1944+ */
1945+ private ProgList getProgListByCenter(String center){
1946+ for (int a=0; a<tvprograms.size(); a++) {
1947+ //
1948+ TVProgram tvp = tvprograms.get(a);
1949+ //
1950+ if (tvp.getType() != ProgType.PROG) {
1951+ continue;
1952+ }
1953+ //
1954+ for (ProgList pl : tvp.getCenters()) {
1955+ if (pl.enabled == true && pl.Center.equals(center)) {
1956+ return pl;
1957+ }
1958+ }
1959+ }
1960+
1961+ return null;
1962+ }
1963+
1964+ /**
1965+ * 指定された放送局の本日の番組一覧を取得する
1966+ *
1967+ * @param center 対象の放送局
1968+ * @return 該当の番組一覧
1969+ */
1970+ private ProgDateList getProgDateListByCenterAndDate(String center, String date){
1971+ ProgList pl = getProgListByCenter(center);
1972+ if (pl == null)
1973+ return null;
1974+
1975+ // 日付ヘッダを描画する
1976+ for (int centerid=0; centerid<pl.pdate.size(); centerid++) {
1977+ ProgDateList pcl = pl.pdate.get(centerid);
1978+
1979+ if (pcl.Date.equals(date))
1980+ return pcl;
1981+ }
1982+
1983+ return null;
1984+ }
1985+
1986+ /**
19011987 * 予約待機赤枠の描画(個々の枠)
19021988 * @see #updateReserveBorder(String)
19031989 */
@@ -1992,7 +2078,7 @@ public abstract class AbsPaperView extends JPanel implements TickTimerListener,H
19922078 continue;
19932079 }
19942080
1995- drawBorder(date,Center,topDateTime,bottomDateTime,tvd.startDateTime,tvd.endDateTime,tvd.length,tuner,env.getPickedColor(),false,q);
2081+ drawBorder(date,Center,topDateTime,bottomDateTime,tvd.startDateTime,tvd.endDateTime,tvd.recmin,tuner,env.getPickedColor(),false,q);
19962082 }
19972083 }
19982084 }
@@ -2248,7 +2334,7 @@ public abstract class AbsPaperView extends JPanel implements TickTimerListener,H
22482334 // 予約枠を描画する
22492335 for (int j=0; j<starts.size(); j++) {
22502336 if (critDateTime.compareTo(ends.get(j)) <= 0) {
2251- list.mergeResTimeItem("", starts.get(j), ends.get(j));
2337+ list.mergeResTimeItem("", starts.get(j), ends.get(j), r, recorder);
22522338 }
22532339 }
22542340 }
@@ -2312,6 +2398,7 @@ public abstract class AbsPaperView extends JPanel implements TickTimerListener,H
23122398 jLayeredPane_space_side_view.add(rb);
23132399
23142400 rb.setVBounds(bounds.getTimebarColumnWidth(), row, length);
2401+ rb.setToolTipText("<html><table>" + item.getTooltip() + "</table></html>");
23152402 rb.setVisible(true);
23162403
23172404 resTimeBorders.add(rb);
@@ -2462,7 +2549,7 @@ public abstract class AbsPaperView extends JPanel implements TickTimerListener,H
24622549 public void moveToPrevDatePage(){
24632550 PassedProgram passed = tvprograms.getPassed();
24642551
2465- String date = passed.getPrevDate(startDate, env.getDatePerPassedPage());
2552+ String date = passed.getPrevDate(startDate, env.getDatePerPassedPage()*this.byCenterModeDayInterval);
24662553 if (date == null)
24672554 return;
24682555
@@ -2478,7 +2565,7 @@ public abstract class AbsPaperView extends JPanel implements TickTimerListener,H
24782565 public boolean isPrevDatePageEnabled(){
24792566 PassedProgram passed = tvprograms.getPassed();
24802567
2481- String date = passed.getPrevDate(startDate, env.getDatePerPassedPage());
2568+ String date = passed.getPrevDate(startDate, env.getDatePerPassedPage()*this.byCenterModeDayInterval);
24822569
24832570 return date != null;
24842571 }
@@ -2492,7 +2579,7 @@ public abstract class AbsPaperView extends JPanel implements TickTimerListener,H
24922579
24932580 PassedProgram passed = tvprograms.getPassed();
24942581
2495- String date = passed.getNextDate(startDate, env.getDatePerPassedPage());
2582+ String date = passed.getNextDate(startDate, env.getDatePerPassedPage()*this.byCenterModeDayInterval);
24962583 if (date == null)
24972584 date = CommonUtils.getDate529(0, true);
24982585
@@ -2507,8 +2594,19 @@ public abstract class AbsPaperView extends JPanel implements TickTimerListener,H
25072594 */
25082595 public boolean isNextDatePageEnabled(){
25092596 String today = CommonUtils.getDate529(0, true);
2597+ if (this.byCenterModeDayInterval == 1){
2598+ return (startDate.compareTo(today) < 0);
2599+ }
2600+
2601+ GregorianCalendar ct = CommonUtils.getCalendar(today);
2602+ ct.add(GregorianCalendar.DAY_OF_MONTH, 7);
2603+ String dateMax = CommonUtils.getDate(ct);
2604+
2605+ ct = CommonUtils.getCalendar(startDate);
2606+ ct.add(GregorianCalendar.DAY_OF_MONTH, env.getDatePerPassedPage()*this.byCenterModeDayInterval);
2607+ String date = CommonUtils.getDate(ct);
25102608
2511- return startDate.compareTo(today) < 0;
2609+ return (date.compareTo(dateMax) <= 0);
25122610 }
25132611
25142612 /*
@@ -2522,6 +2620,68 @@ public abstract class AbsPaperView extends JPanel implements TickTimerListener,H
25222620 return path.getPathComponent(1) == centerNode;
25232621 }
25242622
2623+ /**
2624+ * しょぼかるの番組情報を過去番組表に反映する
2625+ *
2626+ * @param passed 対象の過去番組表
2627+ */
2628+ private void attachSyoboPassed(PassedProgram passed) {
2629+ for ( ProgList tvpl : passed.getCenters() ) {
2630+ attachSyoboPassedProgList(tvpl);
2631+ }
2632+ }
2633+
2634+ /**
2635+ * しょぼかるの番組情報を放送局別過去番組表に反映する
2636+ *
2637+ * @param tvpl 対象の過去番組表
2638+ */
2639+ private void attachSyoboPassedProgList(ProgList tvpl) {
2640+ TVProgram syobo = tvprograms.getSyobo();
2641+ if (syobo == null) {
2642+ return;
2643+ }
2644+
2645+ if ( ! tvpl.enabled) {
2646+ return;
2647+ }
2648+
2649+ for ( ProgList svpl : syobo.getCenters() ) {
2650+ if ( ! tvpl.Center.equals(svpl.Center)) {
2651+ continue;
2652+ }
2653+ for ( ProgDateList tvc : tvpl.pdate ) {
2654+ ProgDateList mSvc = null;
2655+ for ( ProgDateList svc : svpl.pdate ) {
2656+ if (tvc.Date.equals(svc.Date) ) {
2657+ mSvc = svc;
2658+ break;
2659+ }
2660+ }
2661+ if (mSvc == null)
2662+ continue;
2663+
2664+ // しょぼかる側に該当する日付があるのでマッチング。アニメと映画と音楽
2665+ for ( ProgDetailList tvd : tvc.pdetail ) {
2666+ if ( ! tvd.isEqualsGenre(ProgGenre.ANIME, null) &&
2667+ ! tvd.isEqualsGenre(ProgGenre.MOVIE, null) &&
2668+ ! tvd.isEqualsGenre(ProgGenre.MUSIC, null) ) {
2669+ continue;
2670+ }
2671+
2672+ for ( ProgDetailList svd : mSvc.pdetail ) {
2673+ if ( tvd.start.equals(svd.start) ) {
2674+ tvd.linkSyobo = svd.link;
2675+ break;
2676+ }
2677+ }
2678+ }
2679+ }
2680+
2681+ break;
2682+ }
2683+ }
2684+
25252685 /*******************************************************************************
25262686 * ハンドラ―メソッド
25272687 ******************************************************************************/
@@ -3069,12 +3229,17 @@ public abstract class AbsPaperView extends JPanel implements TickTimerListener,H
30693229 }
30703230
30713231 public void mouseClicked(MouseEvent e) {
3232+ JTreeLabel.Nodes node = jLabel_tree.getNode();
3233+ String value = jLabel_tree.getValue();
3234+ if (node == null || value == null)
3235+ return;
3236+
3237+ // 右ダブルクリックで局表示に切り替え
3238+ String center = ((JLabel)e.getSource()).getText();
3239+
30723240 if (e.getButton() == MouseEvent.BUTTON1) {
30733241 if (e.getClickCount() == 2) {
3074- JTreeLabel.Nodes node = jLabel_tree.getNode();
3075- String value = jLabel_tree.getValue();
3076- if (node == null || value == null)
3077- return;
3242+ byCenterModeDayInterval = 1;
30783243
30793244 // 過去ログノードの場合、選択されているノードを開始日とする
30803245 if (node == JTreeLabel.Nodes.PASSED)
@@ -3082,14 +3247,51 @@ public abstract class AbsPaperView extends JPanel implements TickTimerListener,H
30823247 else
30833248 startDate = CommonUtils.getDate529(0, true);
30843249
3085- // 右ダブルクリックで局表示に切り替え
3086- String center = ((JLabel)e.getSource()).getText();
30873250 StdAppendMessage(MSGID+"一局表示に切り替え:"+center);
3088- //redrawByCenter(center);
30893251 jLabel_tree.setView(JTreeLabel.Nodes.BCAST, center);
30903252 reselectTree();
30913253 }
30923254 }
3255+ else if (e.getButton() == MouseEvent.BUTTON3){
3256+ JPopupMenu pop = new JPopupMenu();
3257+ // ポインタの位置
3258+ Point p = e.getPoint();
3259+
3260+ JMenuItem menuItem = new JMenuItem(String.format("放送局別表示【%s】", center));
3261+ Font f = menuItem.getFont();
3262+ menuItem.setFont(f.deriveFont(f.getStyle()|Font.BOLD));
3263+ menuItem.addActionListener(new ActionListener() {
3264+ public void actionPerformed(ActionEvent e) {
3265+ byCenterModeDayInterval = 1;
3266+
3267+ // 過去ログノードの場合、選択されているノードを開始日とする
3268+ if (node == JTreeLabel.Nodes.PASSED)
3269+ startDate = value;
3270+ else
3271+ startDate = CommonUtils.getDate529(0, true);
3272+
3273+ jLabel_tree.setView(JTreeLabel.Nodes.BCAST, center);
3274+ reselectTree();
3275+ }
3276+ });
3277+ pop.add(menuItem);
3278+
3279+ menuItem = new JMenuItem(String.format("放送局別1週おき表示【%s】", center));
3280+ menuItem.addActionListener(new ActionListener() {
3281+ public void actionPerformed(ActionEvent e) {
3282+ byCenterModeDayInterval = 7;
3283+
3284+ // 過去ログノードの場合、選択されているノードを開始日とする
3285+ startDate = value;
3286+
3287+ jLabel_tree.setView(JTreeLabel.Nodes.BCAST, center);
3288+ reselectTree();
3289+ }
3290+ });
3291+ pop.add(menuItem);
3292+
3293+ pop.show((Component)e.getSource(), p.x, p.y);
3294+ }
30933295 }
30943296 };
30953297
--- a/TinyBannavi/src/tainavi/AbsReserveListView.java
+++ b/TinyBannavi/src/tainavi/AbsReserveListView.java
@@ -941,7 +941,7 @@ public abstract class AbsReserveListView extends JPanel implements TickTimerList
941941 ca.add(Calendar.MINUTE, len);
942942 String eDTa = CommonUtils.getDateTime(ca);
943943
944- list.mergeResTimeItem(ra.recorder, sDTa, eDTa);
944+ list.mergeResTimeItem(ra.recorder, sDTa, eDTa, null, null);
945945 }
946946 }
947947
--- a/TinyBannavi/src/tainavi/AbsTitleDialog.java
+++ b/TinyBannavi/src/tainavi/AbsTitleDialog.java
@@ -155,6 +155,7 @@ abstract class AbsTitleDialog extends JDialog {
155155 private boolean folderOnly = false;
156156 private TitleInfo info = null;
157157 private ProgDetailList prog = null;
158+ private ProgDetailList progSyobo = null;
158159 private TVProgramList tvprograms = null;
159160
160161 private boolean reg = false;
@@ -216,14 +217,15 @@ abstract class AbsTitleDialog extends JDialog {
216217 * @param t 編集対象のタイトル情報
217218 * @param tvs フォルダリスト
218219 */
219- public void open(TitleInfo t, ProgDetailList l, String otitle) {
220+ public void open(TitleInfo t, ProgDetailList l, ProgDetailList ls, String otitle) {
220221 info = t;
221222 prog = l;
223+ progSyobo = ls;
222224 recorder = getSelectedRecorder();
223225 tvsFolder = recorder.getFolderList();
224226
225227 updateTitleLabel();
226- updateTitleInfo(t, prog, otitle);
228+ updateTitleInfo(t, prog, progSyobo, otitle);
227229
228230 String device_name = "[" + t.getRec_device() + "]";
229231
@@ -250,7 +252,7 @@ abstract class AbsTitleDialog extends JDialog {
250252 /*
251253 * タイトル情報を更新する
252254 */
253- private void updateTitleInfo(TitleInfo t, ProgDetailList pdl, String titleOld){
255+ private void updateTitleInfo(TitleInfo t, ProgDetailList pdl, ProgDetailList pdlSyobo, String titleOld){
254256 String title = t.getTitle();
255257 jTextField_title.setText(title);
256258
@@ -275,12 +277,33 @@ abstract class AbsTitleDialog extends JDialog {
275277 }
276278 }
277279
280+ if (pdlSyobo != null){
281+ String no = getProgramNo(title, pdlSyobo);
282+ String subtitle = getSubtitle(title, pdlSyobo);
283+ String ptitle = getProgramTitle(title);
284+ String ptitleOld = getProgramTitle(titleOld);
285+
286+ String cand = formatTitleFromOld(ptitleOld, no, subtitle, titleOld);
287+ if (cand != null)
288+ jComboBox_title.addItem(cand);
289+ else{
290+ cand = formatDefaultTitle(ptitle, no, subtitle);
291+ if (cand != null)
292+ jComboBox_title.addItem(cand);
293+ }
294+ }
295+
278296 // 番組情報が見つかったら、タイトルと詳細をセットする
297+ String prog = "";
279298 if (pdl != null){
280- jTextArea_prog.setText(pdl.prefix_mark + pdl.title + pdl.postfix_mark + "\r\n" + pdl.detail);
281- jTextArea_prog.setCaretPosition(0);
299+ prog = pdl.prefix_mark + pdl.title + pdl.postfix_mark + "\r\n" + pdl.detail;
300+ }
301+ if (pdlSyobo != null){
302+ prog += "\r\n【しょぼかる】" + pdlSyobo.prefix_mark + pdlSyobo.title + pdlSyobo.postfix_mark + " " + pdlSyobo.detail;
282303 }
283304
305+ jTextArea_prog.setText(prog);
306+ jTextArea_prog.setCaretPosition(0);
284307 }
285308
286309 /*
--- a/TinyBannavi/src/tainavi/AbsTitleListView.java
+++ b/TinyBannavi/src/tainavi/AbsTitleListView.java
@@ -1130,8 +1130,10 @@ public abstract class AbsTitleListView extends JPanel {
11301130 int bes = tvd.endDateTime.compareTo(start);
11311131
11321132 // 予約情報と時間が重なる場合はその番組情報を返す
1133- if (bse * bes < 0)
1133+ if (bse * bes < 0){
1134+ attachSyobocalToProgDetailList(t, tvd);
11341135 return tvd;
1136+ }
11351137 }
11361138
11371139 break;
@@ -1143,6 +1145,80 @@ public abstract class AbsTitleListView extends JPanel {
11431145 return null;
11441146 }
11451147
1148+ /**
1149+ * 番組情報にしょぼかるの番組詳細リンクをひもづける
1150+ *
1151+ * @param t タイトル情報
1152+ * @param tvd 番組情報
1153+ */
1154+ protected void attachSyobocalToProgDetailList(TitleInfo t, ProgDetailList tvd){
1155+ // すでに番組詳細リンクがある場合は何もしない
1156+ if ( tvd.linkSyobo != null && !tvd.linkSyobo.isEmpty())
1157+ return;
1158+
1159+ ProgDetailList tdl = getSyobocalProgDetailForTitle(t);
1160+ if (tdl != null){
1161+ // 番組詳細リンクをひもづける
1162+ tvd.linkSyobo = tdl.link;
1163+ }
1164+ }
1165+
1166+ /*
1167+ * 指定したタイトルと放送局が同じで時間が重なるしょぼかるの番組情報を取得する
1168+ *
1169+ * @param t タイトル情報
1170+ */
1171+ protected ProgDetailList getSyobocalProgDetailForTitle(TitleInfo t){
1172+ // ジャンルで絞り込む
1173+ if ( ! t.containsGenre(ProgGenre.ANIME.toIEPG()) &&
1174+ ! t.containsGenre(ProgGenre.MOVIE.toIEPG()) &&
1175+ ! t.containsGenre(ProgGenre.MUSIC.toIEPG()) )
1176+ return null;
1177+
1178+ TVProgramList tpl = getTVProgramList();
1179+ TVProgram syobo = tpl.getSyobo();
1180+ if (syobo == null)
1181+ return null;
1182+
1183+ // しょぼかるのチャンネル別番組表を取得する
1184+ ProgList mSvpl = null;
1185+ for ( ProgList svpl : syobo.getCenters() ) {
1186+ if ( t.getCh_name().equals(svpl.Center)) {
1187+ mSvpl = svpl;
1188+ break;
1189+ }
1190+ }
1191+ if (mSvpl == null)
1192+ return null;
1193+
1194+ // しょぼかるの日付別番組表を取得する
1195+ String date = CommonUtils.getDate529(t.getStartDateTime(), true);
1196+ ProgDateList mSvc = null;
1197+ for ( ProgDateList svc : mSvpl.pdate ) {
1198+ if (date.equals(svc.Date) ) {
1199+ mSvc = svc;
1200+ break;
1201+ }
1202+ }
1203+ if (mSvc == null)
1204+ return null;
1205+
1206+ // しょぼかるの番組情報を取得する
1207+ String time = t.getAhh() + ":" + t.getAmm();
1208+ ProgDetailList mSvd = null;
1209+ for ( ProgDetailList svd : mSvc.pdetail ) {
1210+ if ( time.equals(svd.start) ){
1211+ mSvd = svd;
1212+ break;
1213+ }
1214+ }
1215+ if (mSvd == null)
1216+ return null;
1217+
1218+ return mSvd;
1219+
1220+ }
1221+
11461222 /*******************************************************************************
11471223 * リスナー
11481224 ******************************************************************************/
--- a/TinyBannavi/src/tainavi/Env.java
+++ b/TinyBannavi/src/tainavi/Env.java
@@ -1029,6 +1029,11 @@ public class Env {
10291029 public void setMaxSearchWordNum(int n){ maxSearchWordNum = n; }
10301030 private int maxSearchWordNum = MAX_SEARCH_WORD_NUM;
10311031
1032+ // しょぼかるの過去取得日数(タイトル一覧用)
1033+ public int getSyobocalPastDays(){ return syobocalPastDays; }
1034+ public void setSyobocalPastDays(int n){ syobocalPastDays = n; }
1035+ private int syobocalPastDays = 0;
1036+
10321037 /*******************************************************************************
10331038 * 作ったけど使ってないもの
10341039 ******************************************************************************/
--- a/TinyBannavi/src/tainavi/PassedProgram.java
+++ b/TinyBannavi/src/tainavi/PassedProgram.java
@@ -131,7 +131,7 @@ public class PassedProgram extends TVProgramUtils implements TVProgram,Cloneable
131131 /*
132132 * 指定した放送局の期間分の番組情報を読み込む
133133 */
134- public ProgList loadByCenterDates(String startDate, int max, String center){
134+ public ProgList loadByCenterDates(String startDate, int max, int interval, String center){
135135 ProgList pl = new ProgList();
136136 pl.Center = center;
137137 pl.enabled = true;
@@ -159,7 +159,7 @@ public class PassedProgram extends TVProgramUtils implements TVProgram,Cloneable
159159 }
160160
161161 // 日付を1日ずらす
162- c.add(GregorianCalendar.DAY_OF_MONTH, 1);
162+ c.add(GregorianCalendar.DAY_OF_MONTH, interval);
163163 }
164164
165165 return pl;
@@ -707,7 +707,7 @@ public class PassedProgram extends TVProgramUtils implements TVProgram,Cloneable
707707 public String getNextDate(String dateArg, int count){
708708 int num = dd.length;
709709
710- for (int n=0; n<num; n++){
710+ for (int n=0; n<num-1; n++){
711711 String date = dd[num-1-n];
712712
713713 if (date.compareTo(dateArg) <= 0)
--- a/TinyBannavi/src/tainavi/PickedProgram.java
+++ b/TinyBannavi/src/tainavi/PickedProgram.java
@@ -416,7 +416,8 @@ public class PickedProgram extends TVProgramUtils implements TVProgram,Cloneable
416416 }
417417
418418 for (ProgDetailList tPdl : pcl.pdetail) {
419- if (tPdl.title.equals(data.title) && tPdl.start.equals(data.start) && tPdl.end.equals(data.end)) {
419+// if (tPdl.title.equals(data.title) && tPdl.start.equals(data.start) && tPdl.end.equals(data.end)) {
420+ if (tPdl.start.equals(data.start) && tPdl.end.equals(data.end)) {
420421 if (force) {
421422 pcl.pdetail.remove(tPdl);
422423 }
--- a/TinyBannavi/src/tainavi/ResTimeItem.java
+++ b/TinyBannavi/src/tainavi/ResTimeItem.java
@@ -8,27 +8,35 @@ public class ResTimeItem {
88 String start;
99 String end;
1010 int count;
11+ String tooltip;
1112
12- public ResTimeItem(String r, String s, String e, int c){
13+ public ResTimeItem(String r, String s, String e, int c, String t){
1314 this.recorder = r;
1415 this.start = s;
1516 this.end = e;
1617 this.count = c;
18+ this.tooltip = t;
1719 }
1820
19- public ResTimeItem(String r, String s, String e){
21+ public ResTimeItem(String r, String s, String e, String t){
2022 this.recorder = r;
2123 this.start = s;
2224 this.end = e;
2325 this.count = 1;
26+ this.tooltip = t;
2427 }
2528
2629 public void setStart(String s){ start = s; }
2730 public void setEnd(String e){ end = e; }
28- public void addCount(){ count++; }
31+ public void addCount(String t){
32+ count++;
33+ tooltip = tooltip + t;
34+ }
35+ public void setTooltip(String t){ tooltip = t; }
2936
3037 public String getRecorder(){ return recorder; }
3138 public String getStart(){ return start; }
3239 public String getEnd(){ return end; }
3340 public int getCount(){ return count; }
41+ public String getTooltip(){ return tooltip; }
3442 }
--- a/TinyBannavi/src/tainavi/ResTimeList.java
+++ b/TinyBannavi/src/tainavi/ResTimeList.java
@@ -25,13 +25,27 @@ public class ResTimeList extends ArrayList<ResTimeItem> {
2525 /*
2626 * ResTimeItemオブジェクト配列に予約時間枠をマージしながら追加する
2727 */
28- public void mergeResTimeItem(String rec, String start, String end){
28+ public void mergeResTimeItem(String rec, String start, String end, ReserveList rl, HDDRecorder recorder){
2929 Boolean b = false;
3030
3131 // 長さ0の時間枠は無視する
3232 if (start.equals(end))
3333 return;
3434
35+ StringBuilder sb = new StringBuilder("");
36+
37+ sb.append("<TR>");
38+ sb.append("<TD>" + start.substring(11) + "~" + end.substring(11) + "</TD>");
39+ if (rl != null){
40+ sb.append("<TD>" + rl.getCh_name() + "</TD>");
41+ String color = recorder != null ? recorder.getColor(rl.getTuner()) : "000000";
42+ sb.append("<TD><STRONG><FONT COLOR=" + color + ">" + rl.getTuner() + "</FONT></STRONG></TD>");
43+ sb.append("<TD><STRONG><FONT COLOR=BLUE><U>" + rl.getTitle() + "</U></FONT></STRONG></TD>");
44+ }
45+ sb.append("</TR>");
46+
47+ String tooltip = sb.toString();
48+
3549 // すでにある予約枠と順に比較する
3650 for (ResTimeItem item2 : this){
3751 String start2 = item2.getStart();
@@ -60,7 +74,7 @@ public class ResTimeList extends ArrayList<ResTimeItem> {
6074 // <- a ->|<-- item2 -->|
6175 else if (start.compareTo(start2) < 0){
6276 // item2の開始時刻までの枠(a)を再帰呼出しにより追加する
63- mergeResTimeItem(rec, start, start2);
77+ mergeResTimeItem(rec, start, start2, rl, recorder);
6478
6579 // item2の終了時刻より後に終わる場合
6680 // start end
@@ -68,10 +82,10 @@ public class ResTimeList extends ArrayList<ResTimeItem> {
6882 // <- a ->|<-- item2 -->|<- b ->
6983 if (end.compareTo(end2) >= 0){
7084 // item2のカウントを増やす
71- item2.addCount();
85+ item2.addCount(tooltip);
7286
7387 // item2の終了時刻後の枠(b)を再帰呼出しにより追加する
74- mergeResTimeItem(rec, end2, end);
88+ mergeResTimeItem(rec, end2, end, rl, recorder);
7589 }
7690 // item2の終了時刻より前に終わる場合
7791 // start end
@@ -79,12 +93,12 @@ public class ResTimeList extends ArrayList<ResTimeItem> {
7993 // <- a ->|<--- item2 -->|
8094 else{
8195 // item2の終了時間までの枠(c)を追加する
82- ResTimeItem itemT = new ResTimeItem(rec, end, end2, item2.getCount());
96+ ResTimeItem itemT = new ResTimeItem(rec, end, end2, item2.getCount(), item2.getTooltip());
8397 addResTimeItem(itemT);
8498
8599 // item2の時間を短くし、カウントを増やす
86100 item2.setEnd(end);
87- item2.addCount();
101+ item2.addCount(tooltip);
88102 }
89103 }
90104 // item2のの開始時刻より後に始まる場合
@@ -94,7 +108,7 @@ public class ResTimeList extends ArrayList<ResTimeItem> {
94108 else{
95109 // item2の開始時刻からの枠(d)を追加する
96110 if (start.compareTo(start2) > 0){
97- ResTimeItem itemH = new ResTimeItem(rec, start2, start, item2.getCount());
111+ ResTimeItem itemH = new ResTimeItem(rec, start2, start, item2.getCount(), item2.getTooltip());
98112 addResTimeItem(itemH);
99113 }
100114
@@ -106,10 +120,10 @@ public class ResTimeList extends ArrayList<ResTimeItem> {
106120 // item2の開始時刻と長さを変更し、カウントを増やす
107121 item2.setStart(start);
108122 item2.setEnd(end2);
109- item2.addCount();
123+ item2.addCount(tooltip);
110124
111125 // item2の終了時刻後の枠(e)を再帰呼出しにより追加する
112- mergeResTimeItem(rec, end2, end);
126+ mergeResTimeItem(rec, end2, end, rl, recorder);
113127 }
114128 // item2の予約枠の終了時間より前に終わる場合
115129 // start end
@@ -117,13 +131,13 @@ public class ResTimeList extends ArrayList<ResTimeItem> {
117131 // |<----------- item2 ---------->|
118132 else{
119133 // item2の終了時刻前の枠(f)を追加する
120- ResTimeItem itemT = new ResTimeItem(rec, end, end2, item2.getCount());
134+ ResTimeItem itemT = new ResTimeItem(rec, end, end2, item2.getCount(), item2.getTooltip());
121135 addResTimeItem(itemT);
122136
123137 // item2の開始時刻と長さを変更し、カウントを増やす
124138 item2.setStart(start);
125139 item2.setEnd(end);
126- item2.addCount();
140+ item2.addCount(tooltip);
127141 }
128142 }
129143
@@ -133,9 +147,12 @@ public class ResTimeList extends ArrayList<ResTimeItem> {
133147
134148 // 処理が完了しなかった場合は単に追加する
135149 if (!b){
136- ResTimeItem item = new ResTimeItem(rec, start, end);
150+ ResTimeItem item = new ResTimeItem(rec, start, end, tooltip);
137151 addResTimeItem(item);
138152 }
153+
154+ // 開始時刻の昇順になるように並び替える
155+ this.sort(new ResTimeItemComparator());
139156 }
140157
141158 /*
--- a/TinyBannavi/src/tainavi/VersionInfo.java
+++ b/TinyBannavi/src/tainavi/VersionInfo.java
@@ -5,7 +5,7 @@ import java.util.regex.Pattern;
55
66
77 public class VersionInfo {
8- private static final String Version = "タイニー番組ナビゲータ for DBR-T2007 3.22.18β+1.13.5";
8+ private static final String Version = "タイニー番組ナビゲータ for DBR-T2007 3.22.18β+1.13.6";
99
1010 private static final String OSname = System.getProperty("os.name");
1111 private static final String OSvers = System.getProperty("os.version");
--- a/TinyBannavi/src/tainavi/Viewer.java
+++ b/TinyBannavi/src/tainavi/Viewer.java
@@ -1034,6 +1034,7 @@ public class Viewer extends JFrame implements ChangeListener,TickTimerListener,H
10341034 HDDRecorder rec = getSelectedRecorder();
10351035 TitleInfo o = rec.getTitleInfo(ttlId);
10361036 ProgDetailList prog = getProgDetailForTitle(o);
1037+ ProgDetailList progSyobo = getSyobocalProgDetailForTitle(o);
10371038
10381039 String ttlname = o.getTitle();
10391040
@@ -1043,7 +1044,7 @@ public class Viewer extends JFrame implements ChangeListener,TickTimerListener,H
10431044 rec.GetRdTitleDetail(o);
10441045
10451046 VWTitleDialog dlg = new VWTitleDialog(false);
1046- dlg.open(o.clone(), prog, otitle);
1047+ dlg.open(o.clone(), prog, progSyobo, otitle);
10471048 CommonSwingUtils.setLocationCenter(Viewer.this, dlg);
10481049 dlg.setVisible(true);
10491050
@@ -1183,7 +1184,7 @@ public class Viewer extends JFrame implements ChangeListener,TickTimerListener,H
11831184 rec.GetRdTitleDetail(o0);
11841185
11851186 VWTitleDialog dlg = new VWTitleDialog(true);
1186- dlg.open(o0.clone(), null, null);
1187+ dlg.open(o0.clone(), null, null, null);
11871188 CommonSwingUtils.setLocationCenter(Viewer.this, dlg);
11881189 dlg.setVisible(true);
11891190
@@ -5193,6 +5194,9 @@ public class Viewer extends JFrame implements ChangeListener,TickTimerListener,H
51935194 // 祝日情報
51945195 if (env.getUseHolidayCSV())
51955196 HolidayInfo.Load(env.getHolidayFetchURL(), env.getHolidayFetchInterval());
5197+
5198+ // しょぼかるの過去日数
5199+ Syobocal.setPastDays(env.getSyobocalPastDays());
51965200 }
51975201
51985202 // 二重起動チェック
--- a/TinyBannavi/src/tainavi/plugintv/Syobocal.java
+++ b/TinyBannavi/src/tainavi/plugintv/Syobocal.java
@@ -1,529 +1,567 @@
1-package tainavi.plugintv;
2-
3-import java.io.File;
4-import java.util.ArrayList;
5-import java.util.Calendar;
6-import java.util.GregorianCalendar;
7-import java.util.regex.Matcher;
8-import java.util.regex.Pattern;
9-
10-import tainavi.Center;
11-import tainavi.CommonUtils;
12-import tainavi.ContentIdSyobo;
13-import tainavi.ProgDateList;
14-import tainavi.ProgDetailList;
15-import tainavi.ProgList;
16-import tainavi.TVProgram;
17-import tainavi.TVProgramUtils;
18-import tainavi.TraceProgram;
19-
20-/**
21- * しょぼかるから番組表と放送局リストを取得する。プラグインではないよ。
22- */
23-public class Syobocal extends TVProgramUtils implements TVProgram,Cloneable {
24-
25- private final String thisEncoding = "UTF-8";
26-
27- public void setDebug(boolean b) { debug = b; }
28-
29- private boolean debug = false;
30-
31- private boolean rss2 = false;
32-
33- /* 必須コード - ここから */
34-
35- // 種族の特性
36- private static final String tvProgId = "Syobocal";
37-
38- //private final String progCacheFile = getProgDir()+File.separator+"syobocal.xml";
39- private final String centerFile = "env"+File.separator+"center."+getTVProgramId()+".xml";
40-
41- private final String MSGID = "[しょぼかる] ";
42- private final String ERRID = "[ERROR]"+MSGID;
43- private final String DBGID = "[DEBUG]"+MSGID;
44-
45- @Override
46- public String getTVProgramId() { return tvProgId; }
47-
48- @Override
49- public boolean isAreaSelectSupported() { return false; }
50-
51- @Override
52- public ProgType getType() { return ProgType.SYOBO; }
53- public ProgSubtype getSubtype() { return ProgSubtype.NONE; }
54-
55- @Override
56- public Syobocal clone() {
57- return (Syobocal) super.clone();
58- }
59-
60- // 個体の特性
61-
62- //
63- @Override
64- public int getTimeBarStart() {return 5;}
65-
66- private int getDogDays() { return ((getExpandTo8())?(8):(7)); }
67-
68- //
69- @Override
70- public boolean loadProgram(String areaCode, boolean force) {
71-
72- String progCacheFile = null;
73- if ( rss2 ) {
74- progCacheFile = getProgDir()+File.separator+"syobocal.xml";
75- }
76- else {
77- progCacheFile = getProgDir()+File.separator+"syobocal.rss";
78- }
79-
80- // 新しい番組データの入れ物を作る
81- ArrayList<ProgList> newplist = new ArrayList<ProgList>();
82-
83- int cnt = 0;
84-
85- try {
86- String response = null;
87-
88- String cirtDateTimeYMD = CommonUtils.getCritDateTime().replaceAll("[/: ]", "");
89-
90- File f = new File(progCacheFile);
91- if (force == true ||
92- (f.exists() == true && isCacheOld(progCacheFile) == true) ||
93- (f.exists() == false && isCacheOld(null) == true)) {
94-
95- String url = null;
96- if ( rss2 ) {
97- url = "http://cal.syoboi.jp/rss2.php?start="+cirtDateTimeYMD+"&days="+getDogDays()+"&titlefmt=$(Flag)^^^$(FlagW)^^^$(Cat)^^^$(ChName)^^^$(EdTime)^^^$(Title)^^^$(SubTitleB)";
98- }
99- else {
100- url = "http://cal.syoboi.jp/rss.php?start=today&count=1500&days=8&days="+getDogDays()+"&titlefmt=$(Flag)^^^$(FlagW)^^^$(Cat)^^^$(ChName)^^^$(EdTime)^^^$(Title)^^^$(SubTitleB)";
101- }
102- response = webToBuffer(url, thisEncoding, true);
103- if ( response == null ) {
104- reportProgress(ERRID+"RSS2.0(オンライン)の取得に失敗しました: "+url);
105- return false;
106- }
107-
108- reportProgress(MSGID+"RSS2.0(オンライン)を取得しました: "+url);
109- CommonUtils.write2file(progCacheFile, response);
110- }
111- else if (f.exists()) {
112- //
113- response = CommonUtils.read4file(progCacheFile, true);
114- if ( response == null ) {
115- reportProgress(ERRID+"RSS2.0(キャッシュ)の取得に失敗しました: "+progCacheFile);
116- return false;
117- }
118- reportProgress(MSGID+"RSS2.0(キャッシュ)を取得しました: "+progCacheFile);
119- }
120- else {
121- reportProgress(ERRID+"RSS2.0(キャッシュ)がみつかりません: "+progCacheFile);
122- return false;
123- }
124-
125- // 情報解析
126-
127- Matcher ma = Pattern.compile("<item(.+?)</item>",Pattern.DOTALL).matcher(response);
128- while (ma.find()) {
129-
130- // 入れ物
131- ProgDetailList pDetail = new ProgDetailList();
132-
133- // <title>金曜ロードショー ヱヴァンゲリヲン新劇場版:破 TV版</title>
134- Matcher mb = Pattern.compile("<title>(.+?)</title>",Pattern.DOTALL).matcher(ma.group(1));
135- if ( ! mb.find()) {
136- continue;
137- }
138-
139- String[] t = mb.group(1).split("\\^\\^\\^",7);
140-
141- if (t.length < 7) {
142- System.err.println(ERRID+"書式が不正: "+mb.group(1));
143- }
144-
145- pDetail.title = CommonUtils.unEscape(t[5]);
146-
147- pDetail.detail = CommonUtils.unEscape(t[6]);
148- if ( pDetail.detail.matches("^#1$|^#1[^0-9].*$") ) {
149- // まあなにもしなくていいか
150- }
151-
152- if (t[0] != null && t[0].length() > 0) {
153- int flag = Integer.valueOf(t[0]);
154- if (flag != 0) {
155- if ((flag & 0x01) != 0x00) {
156- pDetail.addOption(ProgOption.SPECIAL);
157- flag ^= 0x01;
158- }
159- if ((flag & 0x02) != 0x00) {
160- pDetail.flag = ProgFlags.NEW;
161- flag ^= 0x02;
162- }
163- if ((flag & 0x04) != 0x00) {
164- pDetail.flag = ProgFlags.LAST;
165- flag ^= 0x04;
166- }
167- if ((flag & 0x08) != 0x00) {
168- pDetail.addOption(ProgOption.REPEAT);
169- flag ^= 0x08;
170- }
171- if (flag != 0) {
172- System.out.println(DBGID+"未対応のマーク: "+flag);
173- }
174- }
175- }
176- if (t[1] != null && t[1].length() > 0) {
177- int flagw = Integer.valueOf(t[1]);
178- if (flagw != 0) {
179- if ((flagw & 0x01) != 0x00) {
180- if (pDetail.flag != ProgFlags.NEW && ! pDetail.isOptionEnabled(ProgOption.SPECIAL)) {
181- pDetail.addOption(ProgOption.MOVED); // 新番組や特番なら(移)はいらんやろ
182- }
183- flagw ^= 0x01;
184- }
185- if (flagw != 0) {
186- System.out.println(DBGID+"未対応の警告フラグ: "+flagw);
187- }
188- }
189- }
190- // <tv:genre>アニメ(終了/再放送)</tv:genre>
191- pDetail.genre = null;
192- pDetail.subgenre = null;
193- pDetail.genrelist = new ArrayList<TVProgram.ProgGenre>();
194- pDetail.subgenrelist = new ArrayList<TVProgram.ProgSubgenre>();
195- boolean anime_etc = true;
196-
197- if (t[2] != null && t[2].length() > 0) {
198- // ジャンル
199- if (t[2].equals("1") || t[2].equals("10") || t[2].equals("5")) {
200- // 1:アニメ 10:アニメ(終了/再放送) 5:アニメ関連
201- }
202- else if (t[2].equals("7")) {
203- // 7:OVA
204- pDetail.genre = ProgGenre.ANIME;
205- pDetail.subgenre = ProgSubgenre.ANIME_KOKUNAI;
206- pDetail.genrelist.add(ProgGenre.ANIME);
207- pDetail.subgenrelist.add(ProgSubgenre.ANIME_KOKUNAI);
208- anime_etc = false;
209- }
210- else if (t[2].equals("4")) {
211- // 4:特撮
212- pDetail.genre = ProgGenre.ANIME;
213- pDetail.subgenre = ProgSubgenre.ANIME_TOKUSATSU;
214- pDetail.genrelist.add(ProgGenre.ANIME);
215- pDetail.subgenrelist.add(ProgSubgenre.ANIME_TOKUSATSU);
216- anime_etc = false;
217- }
218- else if (t[2].equals("8")) {
219- // 8:映画
220- pDetail.genre = ProgGenre.MOVIE;
221- pDetail.subgenre = ProgSubgenre.MOVIE_ETC;
222- pDetail.genrelist.add(ProgGenre.MOVIE);
223- pDetail.subgenrelist.add(ProgSubgenre.MOVIE_ETC);
224- pDetail.removeOption(ProgOption.MOVED); // 映画なら(移)はいらんやろ
225- }
226- else if (t[2].equals("0")) {
227- // 0:その他
228- pDetail.genrelist.add(ProgGenre.NOGENRE);
229- pDetail.subgenrelist.add(ProgSubgenre.NOGENRE_ETC);
230- }
231- else if (t[2].equals("3") || t[2].equals("2") || t[2].equals("6")) {
232- // 3:テレビ 2:ラジオ 6:メモ
233- }
234- else {
235- System.out.println(DBGID+"未対応のジャンル: "+t[2]);
236- }
237-
238- // 最後に
239- if (pDetail.genre == null) {
240- pDetail.genre = ProgGenre.ANIME;
241- pDetail.subgenre = ProgSubgenre.ANIME_ETC;
242- }
243- if (pDetail.genrelist.size() == 0 || anime_etc) {
244- pDetail.genrelist.add(ProgGenre.ANIME);
245- pDetail.subgenrelist.add(ProgSubgenre.ANIME_ETC);
246- }
247- }
248-
249- // <dc:publisher>日本テレビ</dc:publisher>
250- if ( t[3] == null || t[3].length() == 0) {
251- System.err.println(ERRID+"放送局名がない: "+mb.group(1));
252- continue;
253- }
254-
255- // ベアな放送局名と、ChannelConvert.datを適用した結果の放送局名
256- String location = CommonUtils.unEscape(t[3]);
257- String modifiedloc = getChName(location);
258-
259- // <tv:startDatetime>2011-08-29T00:00:00+09:00</tv:startDatetime>
260- if ( rss2 ) {
261- mb = Pattern.compile("<pubDate>(.+?)\\+09:00</pubDate>",Pattern.DOTALL).matcher(ma.group(1));
262- }
263- else {
264- mb = Pattern.compile("<tv:startDatetime>(.+?)\\+09:00</tv:startDatetime>",Pattern.DOTALL).matcher(ma.group(1));
265- }
266- if ( ! mb.find()) {
267- System.err.println(ERRID+"開始日時がない");
268- continue;
269- }
270- GregorianCalendar ca = CommonUtils.getCalendar(mb.group(1));
271- if ( ca == null ) {
272- System.err.println(ERRID+"開始日時が不正: "+mb.group(1));
273- continue;
274- }
275-
276- pDetail.startDateTime = CommonUtils.getDateTime(ca);
277- pDetail.start = pDetail.startDateTime.substring(11, 16);
278- pDetail.accurateDate = CommonUtils.getDate(ca);
279-
280- if ( t[4] == null || t[4].length() == 0) {
281- System.err.println(ERRID+"終了時刻がない: "+mb.group(1));
282- continue;
283- }
284-
285- GregorianCalendar cz = CommonUtils.getCalendar(CommonUtils.getDate(ca)+" "+t[4]);
286- if (ca.compareTo(cz) > 0) {
287- cz.add(Calendar.DAY_OF_MONTH, 1);
288- }
289-
290- pDetail.endDateTime = CommonUtils.getDateTime(cz);
291- pDetail.end = pDetail.endDateTime.substring(11, 16);
292-
293- // 24:00~28:59までは前日なんだニャー
294- if (CommonUtils.isLateNight(ca.get(Calendar.HOUR_OF_DAY))) {
295- ca.add(Calendar.DAY_OF_MONTH, -1);
296- }
297-
298- // 番組情報を入れるべき日付
299- String progdate = CommonUtils.getDate(ca);
300-
301- // <description>HD放送</description>
302- mb = Pattern.compile("<description>(.+?)</description>").matcher(ma.group(1));
303- if (mb.find()) {
304- pDetail.detail += " <" + CommonUtils.unEscape(mb.group(1)) + ">";
305- if (pDetail.detail.contains("無料放送")) {
306- pDetail.noscrumble = ProgScrumble.NOSCRUMBLE;
307- }
308- if (pDetail.detail.contains("先行放送")) {
309- pDetail.addOption(ProgOption.PRECEDING);
310- }
311- if (pDetail.detail.contains("変更の可能性")) {
312- pDetail.extension = true;
313- }
314- if ( pDetail.detail.contains("繰り下げ") ) {
315- pDetail.extension = true;
316- }
317- if (pDetail.detail.contains("副音声")) {
318- pDetail.addOption(ProgOption.MULTIVOICE);
319- }
320- }
321-
322- // <link>http://cal.syoboi.jp/tid/44#198593</link>
323- mb = Pattern.compile("<link>(.+?)</link>",Pattern.DOTALL).matcher(ma.group(1));
324- if (mb.find()) {
325- pDetail.link = mb.group(1);
326- if ( ! ContentIdSyobo.isValid(pDetail.link)) {
327- System.out.println(DBGID+"TIDとPIDが取得できない: "+pDetail.link);
328- }
329- }
330-
331- // 追加詳細
332- pDetail.setGenreStr();
333-
334- // 統合
335- {
336- // 放送局が存在するか
337- ProgList prog = null;
338- for (ProgList pl : newplist) {
339- if (pl.Center.equals(modifiedloc)) {
340- prog = pl;
341- break;
342- }
343- }
344- if (prog == null) {
345- // 番組表
346- prog = new ProgList();
347- prog.Center = modifiedloc;
348- prog.enabled = true;
349- prog.pdate = new ArrayList<ProgDateList>();
350-
351- newplist.add(prog);
352- }
353-
354- // 日付が存在するか
355- ProgDateList pCenter = null;
356- for (ProgDateList pcl : prog.pdate) {
357- if (pcl.Date.equals(progdate)) {
358- pCenter = pcl;
359- break;
360- }
361- }
362- if (pCenter == null) {
363- pCenter = new ProgDateList();
364- pCenter.Date = progdate;
365- pCenter.pdetail = new ArrayList<ProgDetailList>();
366- prog.pdate.add(pCenter);
367- }
368-
369- // 連結
370- pCenter.pdetail.add(pDetail);
371-
372- cnt++;
373- }
374-
375- //
376- pDetail.splitted_title = pDetail.title;
377- pDetail.splitted_detail = pDetail.detail;
378-
379- // 詳細を登録する
380- pDetail.titlePop = TraceProgram.replacePop(pDetail.title);
381- pDetail.splitted_titlePop = pDetail.titlePop;
382- pDetail.detailPop = TraceProgram.replacePop(pDetail.detail);
383- pDetail.length = Integer.valueOf(CommonUtils.getRecMin(pDetail.start.substring(0,2),pDetail.start.substring(3,5),pDetail.end.substring(0,2),pDetail.end.substring(3,5)));
384- }
385- }
386- catch (Exception e) {
387- e.printStackTrace();
388- return false;
389- }
390-
391- pcenter = newplist;
392- System.out.println(DBGID+"番組の数: "+cnt);
393- return true;
394- }
395-
396- /* ここまで */
397-
398- /*
399- * ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
400- * ★★★★★ 放送地域を取得する(TVAreaから降格)-ここから ★★★★★
401- * ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
402- */
403-
404- @Override
405- public void loadAreaCode() {
406- }
407-
408- @Override
409- public void saveAreaCode() {
410- }
411-
412- @Override
413- public String getCode(String area) {
414- return "1";
415- }
416-
417- @Override
418- public String getDefaultArea() {
419- return "しょぼかる";
420- }
421-
422- @Override
423- public String getSelectedArea() {
424- return "しょぼかる";
425- }
426-
427- @Override
428- public String getSelectedCode() {
429- return "1";
430- }
431-
432- /*
433- * ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
434- * ★★★★★ 放送地域を取得する(TVAreaから降格)-ここまで ★★★★★
435- * ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
436- */
437-
438- /*
439- * ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
440- * ★★★★★ 放送局を選択する(TVCenterから降格)-ここから ★★★★★
441- * ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
442- */
443-
444- @Override
445- public void loadCenter(String code, boolean force) {
446-
447- if ( code == null ) {
448- System.out.println(ERRID+"地域コードがnullです.");
449- return;
450- }
451-
452- if ( ! force && new File(centerFile).exists() ) {
453- // NOT FORFCEならキャッシュからどうぞ
454- @SuppressWarnings("unchecked")
455- ArrayList<Center> tmp = (ArrayList<Center>) CommonUtils.readXML(centerFile);
456- if ( tmp != null ) {
457- crlist = tmp;
458- return;
459- }
460- }
461-
462- String uri = "http://cal.syoboi.jp/mng?Action=ShowChList";
463- String response = webToBuffer(uri,thisEncoding,true);
464- if ( response == null ) {
465- System.err.println(ERRID+"放送局リストの取得に失敗: "+uri);
466- return;
467- }
468- System.out.println(MSGID+"放送局リストを取得: "+uri);
469-
470- Matcher ma = Pattern.compile("<table class=\"tframe output\".*?>(.+?)</table>",Pattern.DOTALL).matcher(response);
471- if ( ! ma.find() ) {
472- System.err.println(ERRID+"放送局情報がない: "+uri);
473- return;
474- }
475-
476- // 新しい放送局リストの入れ物を作る
477- ArrayList<Center> newcrlist = new ArrayList<Center>();
478-
479- int cnt = 1;
480- Matcher mb = Pattern.compile("<tr>(.+?)</tr>",Pattern.DOTALL).matcher(ma.group(1));
481- while ( mb.find() ) {
482- String[] d = mb.group(1).split("<.*?>",9);
483- if ( d.length != 9 ) {
484- System.err.println(ERRID+"書式不正(カラム数が足りない): "+d.length);
485- continue;
486- }
487- if ( ! d[1].matches("^\\d+$") ) {
488- continue;
489- }
490-
491- // 放送局リスト
492- Center cr = new Center();
493- cr.setLink(d[5]);
494- cr.setAreaCode("1");
495- cr.setCenterOrig(CommonUtils.unEscape(d[7]));
496- //cr.setCenter(this.chconv.get(cr.getCenterOrig())); // ChannelConvert.datで入れ替えたもの
497- cr.setType("");
498- cr.setEnabled(true);
499- cr.setOrder(cnt++);
500-
501- newcrlist.add(cr);
502-
503- if (debug) System.out.println(MSGID+"放送局を追加: "+cr.getCenterOrig()+" -> "+cr.getCenter());
504- }
505-
506- if ( newcrlist.size() == 0 ) {
507- System.err.println(ERRID+"放送局情報の取得結果が0件だったため情報を更新しません");
508- return;
509- }
510-
511- System.out.println(DBGID+"放送局の数: "+newcrlist.size());
512-
513- crlist = newcrlist;
514- attachChFilters();
515- setSortedCRlist();
516- CommonUtils.writeXML(centerFile, crlist);
517- }
518-
519- @Override
520- public boolean saveCenter() {
521- return false;
522- }
523-
524- /*
525- * ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
526- * ★★★★★ 放送局を選択する(TVCenterから降格)-ここまで ★★★★★
527- * ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
528- */
529-}
1+package tainavi.plugintv;
2+
3+import java.io.File;
4+import java.text.SimpleDateFormat;
5+import java.util.ArrayList;
6+import java.util.Calendar;
7+import java.util.GregorianCalendar;
8+import java.util.regex.Matcher;
9+import java.util.regex.Pattern;
10+
11+import tainavi.Center;
12+import tainavi.CommonUtils;
13+import tainavi.ContentIdSyobo;
14+import tainavi.ProgDateList;
15+import tainavi.ProgDetailList;
16+import tainavi.ProgList;
17+import tainavi.TVProgram;
18+import tainavi.TVProgramUtils;
19+import tainavi.TraceProgram;
20+
21+/**
22+ * しょぼかるから番組表と放送局リストを取得する。プラグインではないよ。
23+ */
24+public class Syobocal extends TVProgramUtils implements TVProgram,Cloneable {
25+ private final String thisEncoding = "UTF-8";
26+
27+ public void setDebug(boolean b) { debug = b; }
28+
29+ private boolean debug = false;
30+
31+ private boolean rss2 = false;
32+
33+ /* 必須コード - ここから */
34+
35+ // 種族の特性
36+ private static final String tvProgId = "Syobocal";
37+
38+ private static int pastDays = 0;
39+
40+ public static void setPastDays(int n){ pastDays = n; }
41+
42+ //private final String progCacheFile = getProgDir()+File.separator+"syobocal.xml";
43+ private final String centerFile = "env"+File.separator+"center."+getTVProgramId()+".xml";
44+
45+ private final String MSGID = "[しょぼかる] ";
46+ private final String ERRID = "[ERROR]"+MSGID;
47+ private final String DBGID = "[DEBUG]"+MSGID;
48+
49+ @Override
50+ public String getTVProgramId() { return tvProgId; }
51+
52+ @Override
53+ public boolean isAreaSelectSupported() { return false; }
54+
55+ @Override
56+ public ProgType getType() { return ProgType.SYOBO; }
57+ public ProgSubtype getSubtype() { return ProgSubtype.NONE; }
58+
59+ @Override
60+ public Syobocal clone() {
61+ return (Syobocal) super.clone();
62+ }
63+
64+ // 個体の特性
65+
66+ //
67+ @Override
68+ public int getTimeBarStart() {return 5;}
69+
70+ private int getDogDays() { return ((getExpandTo8())?(8):(7)); }
71+
72+ //
73+ @Override
74+ public boolean loadProgram(String areaCode, boolean force) {
75+
76+ String progCacheFile = null;
77+ if ( rss2 ) {
78+ progCacheFile = getProgDir()+File.separator+"syobocal.xml";
79+ }
80+ else {
81+ progCacheFile = getProgDir()+File.separator+"syobocal.rss";
82+ }
83+
84+ // 新しい番組データの入れ物を作る
85+ ArrayList<ProgList> newplist = new ArrayList<ProgList>();
86+
87+ int cnt = 0;
88+
89+ try {
90+ String response = null;
91+
92+ String cirtDateTimeYMD = CommonUtils.getCritDateTime().replaceAll("[/: ]", "");
93+
94+ File f = new File(progCacheFile);
95+ if (force == true ||
96+ (f.exists() == true && isCacheOld(progCacheFile) == true) ||
97+ (f.exists() == false && isCacheOld(null) == true)) {
98+
99+ String url = null;
100+ if ( rss2 ) {
101+ url = "http://cal.syoboi.jp/rss2.php?start="+cirtDateTimeYMD+"&days="+getDogDays()+"&titlefmt=$(Flag)^^^$(FlagW)^^^$(Cat)^^^$(ChName)^^^$(EdTime)^^^$(Title)^^^$(SubTitleB)";
102+ }
103+ else {
104+// url = "http://cal.syoboi.jp/rss.php?start=today&count=1500&days=8&days="+getDogDays()+"&titlefmt=$(Flag)^^^$(FlagW)^^^$(Cat)^^^$(ChName)^^^$(EdTime)^^^$(Title)^^^$(SubTitleB)";
105+
106+ GregorianCalendar c = new GregorianCalendar();
107+ if ( CommonUtils.isLateNight(c.get(Calendar.HOUR_OF_DAY)) ) {
108+ c.add(Calendar.DAY_OF_MONTH, -1);
109+ }
110+ c.add(Calendar.DAY_OF_MONTH, -pastDays);
111+
112+ String start = new SimpleDateFormat("yyyy-MM-dd").format(c.getTime());
113+ int days = getDogDays() + pastDays;
114+
115+ url = "http://cal.syoboi.jp/rss.php?start="+start+"&count=1500&days="+days+"&titlefmt=$(Flag)^^^$(FlagW)^^^$(Cat)^^^$(ChName)^^^$(EdTime)^^^$(Title)^^^$(SubTitleB)";
116+ }
117+ response = webToBuffer(url, thisEncoding, true);
118+ if ( response == null ) {
119+ reportProgress(ERRID+"RSS2.0(オンライン)の取得に失敗しました: "+url);
120+ return false;
121+ }
122+
123+ reportProgress(MSGID+"RSS2.0(オンライン)を取得しました: "+url);
124+ CommonUtils.write2file(progCacheFile, response);
125+ }
126+ else if (f.exists()) {
127+ //
128+ response = CommonUtils.read4file(progCacheFile, true);
129+ if ( response == null ) {
130+ reportProgress(ERRID+"RSS2.0(キャッシュ)の取得に失敗しました: "+progCacheFile);
131+ return false;
132+ }
133+ reportProgress(MSGID+"RSS2.0(キャッシュ)を取得しました: "+progCacheFile);
134+ }
135+ else {
136+ reportProgress(ERRID+"RSS2.0(キャッシュ)がみつかりません: "+progCacheFile);
137+ return false;
138+ }
139+
140+ // 情報解析
141+
142+ Matcher ma = Pattern.compile("<item(.+?)</item>",Pattern.DOTALL).matcher(response);
143+ while (ma.find()) {
144+
145+ // 入れ物
146+ ProgDetailList pDetail = new ProgDetailList();
147+
148+ // <title>金曜ロードショー ヱヴァンゲリヲン新劇場版:破 TV版</title>
149+ Matcher mb = Pattern.compile("<title>(.+?)</title>",Pattern.DOTALL).matcher(ma.group(1));
150+ if ( ! mb.find()) {
151+ continue;
152+ }
153+
154+ String[] t = mb.group(1).split("\\^\\^\\^",7);
155+
156+ if (t.length < 7) {
157+ System.err.println(ERRID+"書式が不正: "+mb.group(1));
158+ }
159+
160+ pDetail.title = CommonUtils.unEscape(t[5]);
161+
162+ pDetail.detail = CommonUtils.unEscape(t[6]);
163+ if ( pDetail.detail.matches("^#1$|^#1[^0-9].*$") ) {
164+ // まあなにもしなくていいか
165+ }
166+
167+ if (t[0] != null && t[0].length() > 0) {
168+ int flag = Integer.valueOf(t[0]);
169+ if (flag != 0) {
170+ if ((flag & 0x01) != 0x00) {
171+ pDetail.addOption(ProgOption.SPECIAL);
172+ flag ^= 0x01;
173+ }
174+ if ((flag & 0x02) != 0x00) {
175+ pDetail.flag = ProgFlags.NEW;
176+ flag ^= 0x02;
177+ }
178+ if ((flag & 0x04) != 0x00) {
179+ pDetail.flag = ProgFlags.LAST;
180+ flag ^= 0x04;
181+ }
182+ if ((flag & 0x08) != 0x00) {
183+ pDetail.addOption(ProgOption.REPEAT);
184+ flag ^= 0x08;
185+ }
186+ if (flag != 0) {
187+ System.out.println(DBGID+"未対応のマーク: "+flag);
188+ }
189+ }
190+ }
191+ if (t[1] != null && t[1].length() > 0) {
192+ int flagw = Integer.valueOf(t[1]);
193+ if (flagw != 0) {
194+ if ((flagw & 0x01) != 0x00) {
195+ if (pDetail.flag != ProgFlags.NEW && ! pDetail.isOptionEnabled(ProgOption.SPECIAL)) {
196+ pDetail.addOption(ProgOption.MOVED); // 新番組や特番なら(移)はいらんやろ
197+ }
198+ flagw ^= 0x01;
199+ }
200+ if (flagw != 0) {
201+ System.out.println(DBGID+"未対応の警告フラグ: "+flagw);
202+ }
203+ }
204+ }
205+ // <tv:genre>アニメ(終了/再放送)</tv:genre>
206+ pDetail.genre = null;
207+ pDetail.subgenre = null;
208+ pDetail.genrelist = new ArrayList<TVProgram.ProgGenre>();
209+ pDetail.subgenrelist = new ArrayList<TVProgram.ProgSubgenre>();
210+ boolean anime_etc = true;
211+
212+ if (t[2] != null && t[2].length() > 0) {
213+ // ジャンル
214+ if (t[2].equals("1") || t[2].equals("10") || t[2].equals("5")) {
215+ // 1:アニメ 10:アニメ(終了/再放送) 5:アニメ関連
216+ }
217+ else if (t[2].equals("7")) {
218+ // 7:OVA
219+ pDetail.genre = ProgGenre.ANIME;
220+ pDetail.subgenre = ProgSubgenre.ANIME_KOKUNAI;
221+ pDetail.genrelist.add(ProgGenre.ANIME);
222+ pDetail.subgenrelist.add(ProgSubgenre.ANIME_KOKUNAI);
223+ anime_etc = false;
224+ }
225+ else if (t[2].equals("4")) {
226+ // 4:特撮
227+ pDetail.genre = ProgGenre.ANIME;
228+ pDetail.subgenre = ProgSubgenre.ANIME_TOKUSATSU;
229+ pDetail.genrelist.add(ProgGenre.ANIME);
230+ pDetail.subgenrelist.add(ProgSubgenre.ANIME_TOKUSATSU);
231+ anime_etc = false;
232+ }
233+ else if (t[2].equals("8")) {
234+ // 8:映画
235+ pDetail.genre = ProgGenre.MOVIE;
236+ pDetail.subgenre = ProgSubgenre.MOVIE_ETC;
237+ pDetail.genrelist.add(ProgGenre.MOVIE);
238+ pDetail.subgenrelist.add(ProgSubgenre.MOVIE_ETC);
239+ pDetail.removeOption(ProgOption.MOVED); // 映画なら(移)はいらんやろ
240+ }
241+ else if (t[2].equals("0")) {
242+ // 0:その他
243+ pDetail.genrelist.add(ProgGenre.NOGENRE);
244+ pDetail.subgenrelist.add(ProgSubgenre.NOGENRE_ETC);
245+ }
246+ else if (t[2].equals("3") || t[2].equals("2") || t[2].equals("6")) {
247+ // 3:テレビ 2:ラジオ 6:メモ
248+ }
249+ else {
250+ System.out.println(DBGID+"未対応のジャンル: "+t[2]);
251+ }
252+
253+ // 最後に
254+ if (pDetail.genre == null) {
255+ pDetail.genre = ProgGenre.ANIME;
256+ pDetail.subgenre = ProgSubgenre.ANIME_ETC;
257+ }
258+ if (pDetail.genrelist.size() == 0 || anime_etc) {
259+ pDetail.genrelist.add(ProgGenre.ANIME);
260+ pDetail.subgenrelist.add(ProgSubgenre.ANIME_ETC);
261+ }
262+ }
263+
264+ // <dc:publisher>日本テレビ</dc:publisher>
265+ if ( t[3] == null || t[3].length() == 0) {
266+ System.err.println(ERRID+"放送局名がない: "+mb.group(1));
267+ continue;
268+ }
269+
270+ // ベアな放送局名と、ChannelConvert.datを適用した結果の放送局名
271+ String location = CommonUtils.unEscape(t[3]);
272+ String modifiedloc = getChName(location);
273+
274+ // <tv:startDatetime>2011-08-29T00:00:00+09:00</tv:startDatetime>
275+ if ( rss2 ) {
276+ mb = Pattern.compile("<pubDate>(.+?)\\+09:00</pubDate>",Pattern.DOTALL).matcher(ma.group(1));
277+ }
278+ else {
279+ mb = Pattern.compile("<tv:startDatetime>(.+?)\\+09:00</tv:startDatetime>",Pattern.DOTALL).matcher(ma.group(1));
280+ }
281+ if ( ! mb.find()) {
282+ System.err.println(ERRID+"開始日時がない");
283+ continue;
284+ }
285+ GregorianCalendar ca = CommonUtils.getCalendar(mb.group(1));
286+ if ( ca == null ) {
287+ System.err.println(ERRID+"開始日時が不正: "+mb.group(1));
288+ continue;
289+ }
290+
291+ pDetail.startDateTime = CommonUtils.getDateTime(ca);
292+ pDetail.start = pDetail.startDateTime.substring(11, 16);
293+ pDetail.accurateDate = CommonUtils.getDate(ca);
294+
295+ if ( t[4] == null || t[4].length() == 0) {
296+ System.err.println(ERRID+"終了時刻がない: "+mb.group(1));
297+ continue;
298+ }
299+
300+ GregorianCalendar cz = CommonUtils.getCalendar(CommonUtils.getDate(ca)+" "+t[4]);
301+ if (ca.compareTo(cz) > 0) {
302+ cz.add(Calendar.DAY_OF_MONTH, 1);
303+ }
304+
305+ pDetail.endDateTime = CommonUtils.getDateTime(cz);
306+ pDetail.end = pDetail.endDateTime.substring(11, 16);
307+
308+ // 24:00~28:59までは前日なんだニャー
309+ if (CommonUtils.isLateNight(ca.get(Calendar.HOUR_OF_DAY))) {
310+ ca.add(Calendar.DAY_OF_MONTH, -1);
311+ }
312+
313+ // 番組情報を入れるべき日付
314+ String progdate = CommonUtils.getDate(ca);
315+
316+ // <description>HD放送</description>
317+ mb = Pattern.compile("<description>(.+?)</description>").matcher(ma.group(1));
318+ if (mb.find()) {
319+ pDetail.detail += " <" + CommonUtils.unEscape(mb.group(1)) + ">";
320+ if (pDetail.detail.contains("無料放送")) {
321+ pDetail.noscrumble = ProgScrumble.NOSCRUMBLE;
322+ }
323+ if (pDetail.detail.contains("先行放送")) {
324+ pDetail.addOption(ProgOption.PRECEDING);
325+ }
326+ if (pDetail.detail.contains("変更の可能性")) {
327+ pDetail.extension = true;
328+ }
329+ if ( pDetail.detail.contains("繰り下げ") ) {
330+ pDetail.extension = true;
331+ }
332+ if (pDetail.detail.contains("副音声")) {
333+ pDetail.addOption(ProgOption.MULTIVOICE);
334+ }
335+ }
336+
337+ // <link>http://cal.syoboi.jp/tid/44#198593</link>
338+ mb = Pattern.compile("<link>(.+?)</link>",Pattern.DOTALL).matcher(ma.group(1));
339+ if (mb.find()) {
340+ pDetail.link = mb.group(1);
341+ if ( ! ContentIdSyobo.isValid(pDetail.link)) {
342+ System.out.println(DBGID+"TIDとPIDが取得できない: "+pDetail.link);
343+ }
344+ }
345+
346+ // 追加詳細
347+ pDetail.setGenreStr();
348+
349+ // 統合
350+ {
351+ // 放送局が存在するか
352+ ProgList prog = null;
353+ for (ProgList pl : newplist) {
354+ if (pl.Center.equals(modifiedloc)) {
355+ prog = pl;
356+ break;
357+ }
358+ }
359+ if (prog == null) {
360+ // 番組表
361+ prog = new ProgList();
362+ prog.Center = modifiedloc;
363+ prog.enabled = true;
364+ prog.pdate = new ArrayList<ProgDateList>();
365+
366+ newplist.add(prog);
367+ }
368+
369+ // 日付が存在するか
370+ ProgDateList pCenter = null;
371+ for (ProgDateList pcl : prog.pdate) {
372+ if (pcl.Date.equals(progdate)) {
373+ pCenter = pcl;
374+ break;
375+ }
376+ }
377+ if (pCenter == null) {
378+ pCenter = new ProgDateList();
379+ pCenter.Date = progdate;
380+ pCenter.pdetail = new ArrayList<ProgDetailList>();
381+ prog.pdate.add(pCenter);
382+ }
383+
384+ // 連結
385+ pCenter.pdetail.add(pDetail);
386+
387+ cnt++;
388+ }
389+
390+ //
391+ pDetail.splitted_title = pDetail.title;
392+ pDetail.splitted_detail = pDetail.detail;
393+
394+ // 詳細を登録する
395+ pDetail.titlePop = TraceProgram.replacePop(pDetail.title);
396+ pDetail.splitted_titlePop = pDetail.titlePop;
397+ pDetail.detailPop = TraceProgram.replacePop(pDetail.detail);
398+ pDetail.length = Integer.valueOf(CommonUtils.getRecMin(pDetail.start.substring(0,2),pDetail.start.substring(3,5),pDetail.end.substring(0,2),pDetail.end.substring(3,5)));
399+ }
400+ }
401+ catch (Exception e) {
402+ e.printStackTrace();
403+ return false;
404+ }
405+
406+ pcenter = newplist;
407+ System.out.println(DBGID+"番組の数: "+cnt);
408+ return true;
409+ }
410+
411+ /* ここまで */
412+
413+ /*
414+ * ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
415+ * ★★★★★ 放送地域を取得する(TVAreaから降格)-ここから ★★★★★
416+ * ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
417+ */
418+
419+ @Override
420+ public void loadAreaCode() {
421+ }
422+
423+ @Override
424+ public void saveAreaCode() {
425+ }
426+
427+ @Override
428+ public String getCode(String area) {
429+ return "1";
430+ }
431+
432+ @Override
433+ public String getDefaultArea() {
434+ return "しょぼかる";
435+ }
436+
437+ @Override
438+ public String getSelectedArea() {
439+ return "しょぼかる";
440+ }
441+
442+ @Override
443+ public String getSelectedCode() {
444+ return "1";
445+ }
446+
447+ /*
448+ * ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
449+ * ★★★★★ 放送地域を取得する(TVAreaから降格)-ここまで ★★★★★
450+ * ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
451+ */
452+
453+ /*
454+ * ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
455+ * ★★★★★ 放送局を選択する(TVCenterから降格)-ここから ★★★★★
456+ * ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
457+ */
458+
459+ @Override
460+ public void loadCenter(String code, boolean force) {
461+
462+ if ( code == null ) {
463+ System.out.println(ERRID+"地域コードがnullです.");
464+ return;
465+ }
466+
467+ if ( ! force && new File(centerFile).exists() ) {
468+ // NOT FORFCEならキャッシュからどうぞ
469+ @SuppressWarnings("unchecked")
470+ ArrayList<Center> tmp = (ArrayList<Center>) CommonUtils.readXML(centerFile);
471+ if ( tmp != null ) {
472+ crlist = tmp;
473+ return;
474+ }
475+ }
476+
477+ String uri = "http://cal.syoboi.jp/mng?Action=ShowChList";
478+ String response = webToBuffer(uri,thisEncoding,true);
479+ if ( response == null ) {
480+ System.err.println(ERRID+"放送局リストの取得に失敗: "+uri);
481+ return;
482+ }
483+ System.out.println(MSGID+"放送局リストを取得: "+uri);
484+
485+ Matcher ma = Pattern.compile("<table class=\"tframe output\".*?>(.+?)</table>",Pattern.DOTALL).matcher(response);
486+ if ( ! ma.find() ) {
487+ System.err.println(ERRID+"放送局情報がない: "+uri);
488+ return;
489+ }
490+
491+ // 新しい放送局リストの入れ物を作る
492+ ArrayList<Center> newcrlist = new ArrayList<Center>();
493+
494+ int cnt = 1;
495+ Matcher mb = Pattern.compile("<tr>(.+?)</tr>",Pattern.DOTALL).matcher(ma.group(1));
496+ while ( mb.find() ) {
497+ String[] d = mb.group(1).split("<.*?>",9);
498+ if ( d.length != 9 ) {
499+ System.err.println(ERRID+"書式不正(カラム数が足りない): "+d.length);
500+ continue;
501+ }
502+ if ( ! d[1].matches("^\\d+$") ) {
503+ continue;
504+ }
505+
506+ // 放送局リスト
507+ Center cr = new Center();
508+ cr.setLink(d[5]);
509+ cr.setAreaCode("1");
510+ cr.setCenterOrig(CommonUtils.unEscape(d[7]));
511+ //cr.setCenter(this.chconv.get(cr.getCenterOrig())); // ChannelConvert.datで入れ替えたもの
512+ cr.setType("");
513+ cr.setEnabled(true);
514+ cr.setOrder(cnt++);
515+
516+ newcrlist.add(cr);
517+
518+ if (debug) System.out.println(MSGID+"放送局を追加: "+cr.getCenterOrig()+" -> "+cr.getCenter());
519+ }
520+
521+ if ( newcrlist.size() == 0 ) {
522+ System.err.println(ERRID+"放送局情報の取得結果が0件だったため情報を更新しません");
523+ return;
524+ }
525+
526+ System.out.println(DBGID+"放送局の数: "+newcrlist.size());
527+
528+ crlist = newcrlist;
529+ attachChFilters();
530+ setSortedCRlist();
531+ CommonUtils.writeXML(centerFile, crlist);
532+ }
533+
534+ @Override
535+ public boolean saveCenter() {
536+ return false;
537+ }
538+
539+ /*
540+ * ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
541+ * ★★★★★ 放送局を選択する(TVCenterから降格)-ここまで ★★★★★
542+ * ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
543+ */
544+
545+ /**
546+ * 日付変更線(29:00)をまたいだら過去のデータはカットする
547+ * <B> PassedProgramでは使わない
548+ */
549+ @Override
550+ public void refresh() {
551+
552+ String critDate = CommonUtils.getDate529(-60*60*24*pastDays, true);
553+ for ( ProgList p : pcenter ) {
554+ int i = 0;
555+ for ( ProgDateList c : p.pdate ) {
556+ if ( c.Date.compareTo(critDate) >= 0 ) {
557+ break;
558+ }
559+ i++;
560+ }
561+ for ( int j=0; j<i; j++) {
562+ p.pdate.remove(0);
563+ }
564+ }
565+ }
566+
567+}