package tainavi;\r
\r
import java.io.File;\r
+import java.io.UnsupportedEncodingException;\r
+import java.net.URLDecoder;\r
import java.util.ArrayList;\r
-import java.util.Arrays;\r
import java.util.Calendar;\r
import java.util.Date;\r
import java.util.GregorianCalendar;\r
* 定数\r
******************************************************************************/\r
\r
- private static final String XTYPE_BASIC = "basic";\r
- private static final String XTYPE_PREMIUM = "premium";\r
+ private static final String XTYPE_BASIC = "e2";\r
+ private static final String XTYPE_PREMIUM = "HD";\r
\r
private static final String CHNM_PREFIX_BS = "BS";\r
private static final String CHNM_PREFIX_CS = "CS";\r
private static final String CHNM_PREFIX_PR = "Ch.";\r
\r
- private static final String CHID_PREFIX_CS = "e2";\r
- private static final String CHID_PREFIX_HD = "HD";\r
- private static final String CHID_PREFIX_SD = "SD";\r
-\r
- private static final String CHCD_PREFIX_BS = "bs";\r
- private static final String CHCD_PREFIX_CS = "cs";\r
-\r
+ private static final String CHID_PREFIX_BS = "BS";\r
+ private static final String CHID_PREFIX_CS = "CS";\r
+ private static final String CHID_PREFIX_PR = "HD";\r
+ \r
private final String MSGID = "["+getTVProgramId()+"] ";\r
private final String ERRID = "[ERROR]"+MSGID;\r
private final String DBGID = "[DEBUG]"+MSGID;\r
(f.exists() == true && isCacheOld(progCacheFile) == true) ||\r
(f.exists() == false && isCacheOld(null) == true)) {\r
//\r
- String url = null;\r
+ String xtype = (pl.CenterId.startsWith(CHID_PREFIX_BS) || pl.CenterId.startsWith(CHID_PREFIX_CS)) ? XTYPE_BASIC : XTYPE_PREMIUM;\r
+ String chid = xtype != XTYPE_PREMIUM ? pl.CenterId : pl.CenterId.replaceFirst("^"+CHID_PREFIX_PR, "");\r
String dt = CommonUtils.getDateYMD(cal);\r
- url = "http://bangumi.skyperfectv.co.jp/api/version:3/search/date:"+dt.substring(2)+"/channel:"+pl.CenterId+"/?api_key=336eec3423";\r
+ String url = "http://bangumi.skyperfectv.co.jp/"+xtype+"/channel:"+chid+"/date:"+dt.substring(2)+"/";\r
/*\r
if ( pl.ChId.length() == 0 ) {\r
url = "http://bangumi.skyperfectv.co.jp/api/version:3/search/date:"+dt.substring(2)+"/channel:"+pl.CenterId+"/?api_key=336eec3423";\r
\r
ProgDateList pcl = pl.pdate.get(dtidx);\r
\r
- Matcher ma = Pattern.compile("\\{(.+?)\\}[,\\]]",Pattern.DOTALL).matcher(response.replaceFirst("^.+?:\\[", ""));\r
- while ( ma.find() ) {\r
+ String[][] keys = {\r
+ { "class", "pg-title" },\r
+ { "class", "start-time" },\r
+ { "class", "end-time" },\r
+ { "class", "pg-genre" },\r
+ { "class", "pg-explanation" },\r
+ { "id", "actor-name" },\r
+ };\r
+ \r
+ Matcher ma = Pattern.compile("<tbody\\s+id=\"event-\\d+\"[^>]*?>(.+?)</tbody>",Pattern.DOTALL).matcher(response);\r
+ for ( int cnt = 0; ma.find(); cnt++ ) {\r
ProgDetailList pdl = new ProgDetailList();\r
String subtitle = "";\r
String person = "";\r
- Matcher mb = Pattern.compile("\"(.+?)\":(\\[(.*?)\\]|\"?(.*?)\"?)[,}]").matcher(ma.group(1));\r
- while ( mb.find() ) {\r
- if ( mb.group(1).equals("start") && mb.group(2) != null ) {\r
- GregorianCalendar c = new GregorianCalendar();\r
- c.setTimeInMillis(Long.valueOf(mb.group(2).replaceFirst("\\.\\d+$", ""))*1000L);\r
- pdl.accurateDate = CommonUtils.getDate(c);\r
- pdl.startDateTime = CommonUtils.getDateTime(c); \r
- pdl.start = CommonUtils.getTime(c).replaceFirst("^.+ ", "");\r
- }\r
- else if ( mb.group(1).equals("end") && mb.group(2) != null ) {\r
- GregorianCalendar c = new GregorianCalendar();\r
- c.setTimeInMillis(Long.valueOf(mb.group(2).replaceFirst("\\.\\d+$", ""))*1000L);\r
- pdl.endDateTime = CommonUtils.getDateTime(c); \r
- pdl.end = CommonUtils.getTime(c).replaceFirst("^.+ ", "");\r
- }\r
- else if ( mb.group(1).equals("title") && mb.group(4) != null ) {\r
- pdl.title = mb.group(4).replace("\\\"", "\"");\r
- }\r
- else if ( mb.group(1).equals("episode_title") && mb.group(4) != null ) {\r
- subtitle = mb.group(4);\r
- }\r
- else if ( mb.group(1).equals("explanation") && mb.group(4) != null ) {\r
- pdl.detail = mb.group(4).replace("\\\"", "\"").replace("\\n", "\n");\r
- }\r
- else if ( mb.group(1).equals("person") && mb.group(3) != null ) {\r
- String[] d = mb.group(3).split(",");\r
- for ( String s : d ) {\r
- Matcher mc = Pattern.compile("\"(.+?)\"").matcher(s);\r
- if ( mc.find() ) {\r
- person += "、"+mc.group(1);\r
- }\r
+ \r
+ for ( String[] k : keys ) {\r
+ Matcher mb = Pattern.compile("<span\\s+"+k[0]+"=\""+k[1]+"\"[^>]*?>\\s*(.+?)\\s*</span>",Pattern.DOTALL).matcher(ma.group(1));\r
+ while ( mb.find() ) {\r
+ if ( mb.group(1) == null ) {\r
+ continue;\r
}\r
- if ( person.length() > 0 ) {\r
- person = person.substring(1);\r
+ \r
+ if ( k[1].equals("pg-title") ) {\r
+ pdl.title = CommonUtils.unEscape(mb.group(1)).trim();\r
}\r
- }\r
- else if ( mb.group(1).equals("duration") ) {\r
- //pdl.length = Integer.valueOf(mb.group(4))/60; // 使えないっぽい\r
- }\r
- else if ( mb.group(1).equals("genres") && mb.group(3) != null ) {\r
- Matcher mc = Pattern.compile("\\[\"(.*?)\",\"(.*?)\"[,\\]]", Pattern.DOTALL).matcher(mb.group(3));\r
- if ( mc.find() ) {\r
+ else if ( k[1].equals("start-time") ) {\r
+ pdl.start = mb.group(1);\r
+ }\r
+ else if ( k[1].equals("end-time") ) {\r
+ pdl.end = mb.group(1);\r
\r
- String grstr = mc.group(1).replaceAll("/", "/");\r
- ProgGenre gr = ProgGenre.get(grstr);\r
- if ( gr == null ) {\r
- // 未定義のジャンルです!\r
- gr = ProgGenre.NOGENRE;\r
- gf.put(mc.group(1),null);\r
- }\r
- if ( pdl.genre == null || (pdl.genre == ProgGenre.NOGENRE && gr != ProgGenre.NOGENRE) ) {\r
- pdl.genre = gr;\r
+ GregorianCalendar c = CommonUtils.getCalendar(pcl.Date);\r
+ \r
+ if ( cnt == 0 && pdl.start.compareTo(pdl.end) > 0 ) {\r
+ c.add(Calendar.DATE, -1);\r
}\r
+ pdl.accurateDate = CommonUtils.getDate(c);\r
+ pdl.startDateTime = CommonUtils.getDate(c,false)+" "+pdl.start;\r
\r
- String sgstr = mc.group(2).replaceAll("ィー", "ィ");\r
- ProgSubgenre sg = ProgSubgenre.get(gr, sgstr);\r
- if ( sg == null ) {\r
- // 未定義のサブジャンルです!\r
- ArrayList<ProgSubgenre> vals = ProgSubgenre.values(gr);\r
- sg = vals.get(vals.size()-1);\r
+ if ( pdl.start.compareTo(pdl.end) > 0 ) {\r
+ c.add(Calendar.DATE, 1);\r
}\r
- pdl.subgenre = sg;\r
+ pdl.endDateTime = CommonUtils.getDate(c,false)+" "+pdl.end;\r
+ \r
+ pdl.length = CommonUtils.getRecMinVal(pdl.start, pdl.end);\r
}\r
- else {\r
- pdl.genre = ProgGenre.NOGENRE;\r
- pdl.subgenre = ProgSubgenre.NOGENRE_ETC;\r
+ else if ( k[1].equals("pg-genre") ) {\r
+ Matcher mc = Pattern.compile("/large_genre:(.+?)/medium_genre:(.+?)/",Pattern.DOTALL).matcher(mb.group(1));\r
+ if ( mc.find() ) {\r
+ try {\r
+ String grstr = URLDecoder.decode(mc.group(1),"utf8").replaceAll("/", "/");\r
+ ProgGenre gr = ProgGenre.get(grstr);\r
+ if ( gr == null ) {\r
+ gr = genremap.get(grstr);\r
+ if (gr == null) {\r
+ // 未定義のジャンルです!\r
+ gr = ProgGenre.NOGENRE;\r
+ gf.put(grstr,null);\r
+ }\r
+ }\r
+ if ( pdl.genre == null || (pdl.genre == ProgGenre.NOGENRE && gr != ProgGenre.NOGENRE) ) {\r
+ pdl.genre = gr;\r
+ }\r
+ \r
+ String sgstr = URLDecoder.decode(mc.group(2),"utf8").replaceAll("ィー", "ィ");\r
+ ProgSubgenre sg = ProgSubgenre.get(gr, sgstr);\r
+ if ( sg == null ) {\r
+ // 未定義のサブジャンルです!\r
+ ArrayList<ProgSubgenre> vals = ProgSubgenre.values(gr);\r
+ sg = vals.get(vals.size()-1);\r
+ }\r
+ pdl.subgenre = sg;\r
+ } catch (UnsupportedEncodingException e) {\r
+ e.printStackTrace();\r
+ pdl.genre = ProgGenre.NOGENRE;\r
+ pdl.subgenre = ProgSubgenre.NOGENRE_ETC;\r
+ }\r
+ }\r
+ else {\r
+ pdl.genre = ProgGenre.NOGENRE;\r
+ pdl.subgenre = ProgSubgenre.NOGENRE_ETC;\r
+ }\r
}\r
- }\r
- else if ( mb.group(1).equals("no_scramble") && mb.group(4) != null ) {\r
- pdl.noscrumble = (mb.group(4).equals("ノンスクランブル"))?(ProgScrumble.NOSCRUMBLE):(ProgScrumble.SCRUMBLED);\r
- }\r
- else if ( mb.group(1).equals("caption_dubbing") && mb.group(4) != null ) {\r
- if ( mb.group(4).contains("字幕") ) {\r
- pdl.option.add(ProgOption.SUBTITLE);\r
+ else if ( k[1].equals("pg-explanation") ) {\r
+ pdl.detail += CommonUtils.decBr(CommonUtils.unEscape(mb.group(1))).trim()+"\n";\r
+ }\r
+ else if ( k[1].equals("actor-name") ) {\r
+ person += "、"+CommonUtils.unEscape(mb.group(1)).trim();\r
}\r
}\r
}\r
\r
- // 算出してみる\r
- pdl.length = CommonUtils.getRecMinVal(pdl.start, pdl.end);\r
- //(int)(CommonUtils.getDiffDateTime(pdl.startDateTime, pdl.endDateTime)/60000L);\r
+ // 出演者\r
+ if ( person.length() > 0 ) {\r
+ person = person.substring(1);\r
+ }\r
\r
// くっつけてみる\r
pdl.detail =\r
+((person.length()>0)?(DETAIL_SEP+person):(""));\r
pdl.detail = pdl.detail.replaceFirst("[\r\n]+$", "");\r
\r
+ Matcher mb = Pattern.compile("<img\\s+src=\"/i/icon_(.+?)\\.gif",Pattern.DOTALL).matcher(ma.group(1));\r
+ while ( mb.find() ) {\r
+ if ( mb.group(1).equals("5.1") ) {\r
+ pdl.addOption(ProgOption.SURROUND);\r
+ }\r
+ else if ( mb.group(1).equals("jimaku") ) {\r
+ pdl.addOption(ProgOption.SUBTITLE);\r
+ }\r
+ else if ( mb.group(1).equals("2kakoku") ) {\r
+ pdl.addOption(ProgOption.BILINGUAL);\r
+ }\r
+ else if ( mb.group(1).equals("fukikae") ) {\r
+ pdl.addOption(ProgOption.STANDIN);\r
+ }\r
+ else if ( mb.group(1).equals("tajuu") ) {\r
+ pdl.addOption(ProgOption.MULTIVOICE);\r
+ }\r
+ else if ( mb.group(1).equals("r15") || mb.group(1).equals("r18") || mb.group(1).equals("adult") ) {\r
+ pdl.addOption(ProgOption.RATING);\r
+ }\r
+ else if ( mb.group(1).equals("ppv") ) {\r
+ pdl.addOption(ProgOption.PV);\r
+ }\r
+ else if ( mb.group(1).equals("nama") ) {\r
+ pdl.addOption(ProgOption.LIVE);\r
+ }\r
+ else {\r
+ nf.put(mb.group(1), null);\r
+ }\r
+ }\r
+ \r
// タイトルから各種フラグを分離する\r
doSplitFlags(pdl, nf);\r
\r
ArrayList<Center> tmp = (ArrayList<Center>)CommonUtils.readXML(centerListFile);\r
if ( tmp != null ) {\r
crlist = tmp;\r
+\r
+ String[][] prs = {\r
+ {CHNM_PREFIX_BS, CHID_PREFIX_BS},\r
+ {CHNM_PREFIX_CS, CHID_PREFIX_CS},\r
+ {CHNM_PREFIX_PR, CHID_PREFIX_PR},\r
+ };\r
+ \r
+ // 2013.11 新旧変換\r
+ for ( Center cr : crlist ) {\r
+ for ( String[] a : prs ) {\r
+ if ( cr.getCenterOrig().startsWith(a[0]) ) {\r
+ if ( ! cr.getLink().startsWith(a[1]) ) {\r
+ Matcher ma = Pattern.compile("^.*?(\\d+)",Pattern.DOTALL).matcher(cr.getCenterOrig());\r
+ if ( ma.find() ) {\r
+ String chid = a[1]+ma.group(1);\r
+ System.err.println(DBGID+"converted: "+cr.getCenterOrig()+", "+cr.getLink()+" -> "+chid);\r
+ cr.setLink(chid);\r
+ }\r
+ }\r
+ \r
+ break;\r
+ }\r
+ }\r
+ }\r
+ \r
// 放送局名変換\r
attachChFilters();\r
System.out.println("放送局リストを読み込みました: "+centerListFile);\r
\r
ArrayList<Center> crl = new ArrayList<Center>();\r
\r
- String url = String.format("http://www.skyperfectv.co.jp/channel/%s/list.html",xtype);\r
+ String url = String.format("http://bangumi.skyperfectv.co.jp/index/channel/%s/",xtype);\r
if (getDebug()) System.err.println(DBGID+"get page: "+url);\r
String response = webToBuffer(url,thisEncoding,true);\r
if ( response == null ) {\r
return null;\r
}\r
\r
- Matcher ma = Pattern.compile("<tr class=\"[^\"]*?ch-(.)d\">(.+?)</tr>",Pattern.DOTALL).matcher(response);\r
+ Matcher ma = Pattern.compile("<td class=\"channel-icon\">\\s*<a href=\".*?/channel:(.+?)/\".*?>\\s*<img src=\".*?\" alt=\"(.+?)\"",Pattern.DOTALL).matcher(response);\r
while ( ma.find() ) {\r
- Matcher mb = Pattern.compile("<div class=\"channel ch-channel\">.+?<a href=\"/channel/"+xtype+"/(bs|cs)?(\\d+)\\.html\">[ \\t]*(.+?)[ \\t]*</a>",Pattern.DOTALL).matcher(ma.group(2));\r
- if ( mb.find() ) {\r
- String chid_prefix = null;\r
- String chnm_prefix = null;\r
- if ( xtype.equals(XTYPE_BASIC) && CHCD_PREFIX_CS.equals(mb.group(1)) ) {\r
- chid_prefix = CHID_PREFIX_CS;\r
- chnm_prefix = CHNM_PREFIX_CS;\r
- }\r
- else {\r
- if ( xtype.equals(XTYPE_BASIC) ) {\r
- chid_prefix = CHID_PREFIX_CS;\r
- }\r
- else {\r
- if ( ma.group(1).equals("h") ) {\r
- chid_prefix = CHID_PREFIX_HD;\r
- }\r
- else {\r
- chid_prefix = CHID_PREFIX_SD;\r
- }\r
- }\r
- if ( CHCD_PREFIX_BS.equals(mb.group(1)) ) {\r
- chnm_prefix = CHNM_PREFIX_BS;\r
- }\r
- else {\r
- chnm_prefix = CHNM_PREFIX_PR;\r
- }\r
- }\r
+ String chid = (xtype.equals(XTYPE_PREMIUM) ? CHID_PREFIX_PR : "") + ma.group(1);\r
+ String chnm = CommonUtils.toHANALNUM(CommonUtils.unEscape(ma.group(2))).replaceFirst("[ \\t]+▲$", "");\r
\r
- if ( chid_prefix != null ) {\r
- Center cr = new Center();\r
- cr.setAreaCode(areacode);\r
- cr.setType("");\r
- cr.setEnabled(true);\r
- String name = CommonUtils.toHANALNUM(CommonUtils.unEscape(chnm_prefix+mb.group(2)+" "+mb.group(3)));\r
- String chid = null;\r
- if ( xtype.equals(XTYPE_BASIC) && name.contains("スター・チャンネル") ) {\r
- // e2のスター・チャンネルは専用の番組表が用意されていないみたい\r
- chid = String.format("%s%03d",CHID_PREFIX_HD,Integer.valueOf(mb.group(2))+425);\r
- }\r
- else {\r
- chid = chid_prefix+mb.group(2);\r
- }\r
- if ( ! name.endsWith("▲") && chid.startsWith("SD") ) {\r
- // スカパーのサイトにバグがあった!\r
- chid = chid.replaceFirst("^SD", "HD");\r
- if (getDebug()) System.err.println("+コード修正: "+name+" (SD->"+chid+")");\r
- }\r
- cr.setCenterOrig(name.replaceFirst("[ \\t]+▲$", ""));\r
- cr.setLink(chid);\r
- \r
- int idx = 0;\r
- for ( Center ct : crl ) {\r
- if ( ct.getCenterOrig().compareTo(cr.getCenterOrig()) > 0 ) {\r
- break;\r
- }\r
- idx++;\r
- }\r
- crl.add(idx, cr);\r
- \r
- if (getDebug()) System.err.println(DBGID+"center: "+cr.getCenterOrig()+", "+cr.getLink());\r
+ // 統一性がない\r
+ if ( xtype.equals(XTYPE_PREMIUM) && ! chnm.startsWith(CHNM_PREFIX_PR) ) {\r
+ chnm = CHNM_PREFIX_PR+chnm;\r
+ }\r
+ \r
+ Center cr = new Center();\r
+ cr.setAreaCode(areacode);\r
+ cr.setType("");\r
+ cr.setEnabled(true);\r
+ cr.setCenterOrig(chnm);\r
+ cr.setLink(chid);\r
+ \r
+ int idx = 0;\r
+ for ( Center ct : crl ) {\r
+ if ( ct.getCenterOrig().compareTo(cr.getCenterOrig()) > 0 ) {\r
+ break;\r
}\r
+ idx++;\r
}\r
+ crl.add(idx, cr);\r
+ \r
+ if (getDebug()) System.err.println(DBGID+"center: "+cr.getCenterOrig()+", "+cr.getLink());\r
}\r
\r
return crl;\r
+++ /dev/null
-\r
-package tainavi;\r
-\r
-import java.io.File;\r
-import java.util.ArrayList;\r
-import java.util.Calendar;\r
-import java.util.Date;\r
-import java.util.GregorianCalendar;\r
-import java.util.HashMap;\r
-import java.util.LinkedHashMap;\r
-import java.util.regex.Matcher;\r
-import java.util.regex.Pattern;\r
-\r
-\r
-public class PlugIn_TVPMSN extends TVProgramUtils implements TVProgram,Cloneable {\r
-\r
- private static final String thisEncoding = "UTF-8"; \r
- \r
- /* 必須コード - ここから */\r
- \r
- // 種族の特性\r
- @Override\r
- public String getTVProgramId() { return "MSNエンタメ"; }\r
- \r
- @Override\r
- public ProgType getType() { return ProgType.PROG; }\r
- @Override\r
- public ProgSubtype getSubtype() { return ProgSubtype.TERRA; }\r
-\r
- //\r
- @Override\r
- public PlugIn_TVPMSN clone() {\r
- return (PlugIn_TVPMSN) super.clone();\r
- }\r
- \r
- // 個体の特性\r
-\r
- @Override\r
- public int getTimeBarStart() {return 5;}\r
- \r
- private int getDogDays() { return ((getExpandTo8())?(8):(7)); }\r
-\r
- private static final int bscntMax = 10;\r
- private int bscnt = 4;\r
- public void setBscnt(int n) { bscnt = n; }\r
- \r
- private String getBscntFile() { return String.format("env"+File.separator+"bscnt.%s",getTVProgramId()); }\r
- \r
- /*******************************************************************************\r
- * 定数\r
- ******************************************************************************/\r
- \r
- private final String MSGID = "["+getTVProgramId()+"] ";\r
- private final String ERRID = "[ERROR]"+MSGID;\r
- private final String DBGID = "[DEBUG]"+MSGID;\r
- \r
- // 新しい入れ物の臨時格納場所\r
- private ArrayList<ProgList> newplist = null;\r
- \r
- private HashMap<String,String> nf = null;\r
- \r
- \r
- @Override\r
- public void loadProgram(String areaCode, boolean force) {\r
- \r
- // 新しい入れ物(トップ)を用意する\r
- newplist = new ArrayList<ProgList>();\r
- \r
- nf = new HashMap<String, String>();\r
- \r
- // 地域コードごとの参照ページ数の入れ物を用意する\r
- LinkedHashMap<String,Integer> pages = new LinkedHashMap<String, Integer>();\r
- \r
- // 参照する地域コードをまとめる\r
- if ( areaCode.equals(allCode) ) {\r
- // 「全国」\r
- for ( Center cr : crlist ) {\r
- if ( cr.getOrder() > 0 ) {\r
- // 有効局の地域コードのみ集める\r
- pages.put(cr.getAreaCode(),0);\r
- }\r
- }\r
- }\r
- else {\r
- // 地域個別\r
- pages.put(areaCode,0);\r
- pages.put(bsCode,0);\r
- }\r
- \r
- // トップの下に局ごとのリストを生やす\r
- for ( String ac : pages.keySet() ) {\r
- for ( Center cr : crlist ) {\r
- if ( ac.equals(cr.getAreaCode()) ) {\r
- ProgList pl = new ProgList();\r
- pl.Area = cr.getAreaCode();\r
- pl.SubArea = cr.getType();\r
- pl.Center = cr.getCenter();\r
- pl.BgColor = cr.getBgColor();\r
- \r
- // <TABLE>タグの列数を決め打ちで処理するので、設定上無効な局も内部的には列の1つとして必要\r
- pl.enabled = (cr.getOrder()>0)?(true):(false);\r
- \r
- newplist.add(pl);\r
- \r
- int pg = Integer.valueOf(cr.getType());\r
- if ( pl.enabled && pages.get(ac) < pg ) {\r
- // 地域コードごとの最大参照ページ数を格納する\r
- pages.put(ac,pg);\r
- }\r
- }\r
- }\r
- }\r
-\r
- // 局の下に日付ごとのリストを生やす\r
- GregorianCalendar cal = new GregorianCalendar();\r
- cal.setTime(new Date());\r
- if ( CommonUtils.isLateNight(cal) ) {\r
- // 4時までは当日扱いにする\r
- cal.add(Calendar.DATE, -1);\r
- }\r
- GregorianCalendar cale = (GregorianCalendar) cal.clone();\r
- for (int i=0; i<getDogDays(); i++) {\r
- String date = CommonUtils.getDate(cale);\r
- for ( ProgList pl : newplist ) {\r
- ProgDateList cl = new ProgDateList();\r
- cl.Date = date;\r
- pl.pdate.add(cl);\r
- }\r
- cale.add(Calendar.DATE, 1);\r
- }\r
-\r
- // 参照する総ページ数を計算\r
- int counterMax = 0;\r
- for ( String ac : pages.keySet() ) {\r
- counterMax += pages.get(ac)*getDogDays();\r
- }\r
- \r
- // 日付の下に番組情報ごとのリストを生やす(MSNは1ページに複数局存在する)\r
- int counter = 1;\r
- for ( String ac : pages.keySet() ) {\r
- cale = (GregorianCalendar) cal.clone();\r
- for ( int i=0; i<getDogDays(); i++ ) {\r
- String date = CommonUtils.getDateYMD(cale);\r
- for ( int d=1; d<=pages.get(ac) && d<=REFPAGESMAX; d++ ) { // 最大{REFPAGESMAX}ページまでしか参照しない\r
- String url;\r
- if ( ac.equals(bsCode) ) {\r
- url = "http://program.tv.jp.msn.com/tv.php?site=032&mode=06&category=d"+d+"&area="+ac+"&template=program&sdate="+date+"&shour=05&lhour=24";\r
- }\r
- else {\r
- if ( d == 1 ) {\r
- url = "http://program.tv.jp.msn.com/tv.php?site=032&mode=06&category=g&area="+ac+"&template=program&sdate="+date+"&shour=05&lhour=24";\r
- }\r
- else {\r
- url = "http://program.tv.jp.msn.com/tv.php?site=032&mode=06&category=s&area="+ac+"&template=program&sdate="+date+"&shour=05&lhour=24";\r
- }\r
- }\r
- _loadProgram(ac, String.valueOf(d), url, force, i, cale.get(Calendar.MONTH)+1, cale.get(Calendar.DATE), counter++, counterMax);\r
- }\r
- \r
- cale.add(Calendar.DATE, 1);\r
- }\r
- }\r
- \r
- // 開始・終了日時を正しい値に計算しなおす\r
- for (ProgList pl : newplist) {\r
- setAccurateDate(pl.pdate);\r
- }\r
- \r
- // 解析用\r
- {\r
- for ( String f : nf.keySet() ) {\r
- System.err.println(String.format("【デバッグ情報】未定義のフラグは[?]と表示されます。: [%s]",f));\r
- }\r
- }\r
- \r
- // 古い番組データを置き換える\r
- pcenter = newplist;\r
- }\r
- \r
- /* ここまで */\r
- \r
- \r
- \r
- /*\r
- * 非公開メソッド等\r
- */\r
- \r
- private void _loadProgram(String areacode, String page, String url, boolean force, int wdaycol, int month, int day, int counter, int counterMax) {\r
- // progfilesの読み出し\r
- //\r
- final String progCacheFile = String.format(getProgDir()+File.separator+"TVMSN_%s_%s_%04d.html", areacode, page, day);\r
- try {\r
- File f = new File(progCacheFile);\r
- if (force == true ||\r
- (f.exists() == true && isCacheOld(progCacheFile) == true) ||\r
- (f.exists() == false && isCacheOld(null) == true)) {\r
- webToFile(url, progCacheFile, thisEncoding);\r
- reportProgress(String.format("%s(%s)を取得しました[%s-%02d日-%sページ]: (%d/%d) %s",getTVProgramId(),"オンライン",getArea(areacode),day,page,counter,counterMax,url));\r
- }\r
- else if (CommonUtils.isFileAvailable(f,10)) {\r
- reportProgress(String.format("%s(%s)を取得しました[%s-%02d日-%sページ]: (%d/%d) %s",getTVProgramId(),"キャッシュ",getArea(areacode),day,page,counter,counterMax,url));\r
- }\r
- else {\r
- reportProgress(String.format("%s(%s)がみつかりません[%s-%02d日-%sページ]: (%d/%d) %s",getTVProgramId(),"キャッシュ",getArea(areacode),day,page,counter,counterMax,url));\r
- return;\r
- }\r
-\r
- // キャッシュファイルの読み込み\r
- String response = CommonUtils.read4file(progCacheFile, false);\r
- \r
- // キャッシュが不整合を起こしていたら投げ捨てる\r
- Matcher ma = Pattern.compile(String.format("<h1 class=\"headtext\">%d月%d日",month,day)).matcher(response);\r
- if ( ! ma.find() ) {\r
- reportProgress(getTVProgramId()+"(キャッシュ)が無効です: ("+counter+"/"+counterMax+") "+progCacheFile);\r
- return;\r
- }\r
- \r
- // 番組リストの追加\r
- getPrograms(areacode, page, wdaycol, response);\r
- }\r
- catch (Exception e) {\r
- // 例外\r
- }\r
- }\r
- \r
- //\r
- \r
- private void getPrograms(String areacode, String page, int wdaycol, String src) {\r
- Matcher ma = Pattern.compile("\n(<TD ROWSPAN =(\\d+) CLASS = \".*?\"> </TD>|<td width=\".*?\" valign=\".*?\" id=\".*?\" ROWSPAN =.*?</A></DIV></TD>)").matcher(src);\r
- while (ma.find()) {\r
- \r
- \r
- //\r
- ProgDetailList pdl = new ProgDetailList();\r
- //\r
- if (ma.group(1).startsWith("<TD")) {\r
- //System.err.println(ma.group(1));\r
- pdl.title = "番組情報がありません";\r
- pdl.length = Integer.valueOf(ma.group(2));\r
- }\r
- else {\r
- // 1 : length\r
- // 2 : genre\r
- // 3 : link\r
- // 4 : title\r
- // 5,6 : start-hour,min\r
- // 7,8 : end-hour,min\r
- // 9,10 : detail1,detail2\r
- Matcher mb = Pattern.compile("<td width=\".*?\" valign=\".*?\" id=\".*?\" ROWSPAN =(\\d+) CLASS = \"(.*?)\"><DIV [^>]*?><a href=.*?onClick=\"xClickACT\\(\'\\.(.+?)\'\\);.*?<h1>(.*?)</h1><h2>.*?(\\d\\d):(\\d\\d)~(\\d\\d):(\\d\\d).*?</h2><p>(.*?)</p><p>(.*?)</p>.*?</A></DIV></TD>").matcher(ma.group(1));\r
- if ( ! mb.find() ) {\r
- System.err.println("TVMSN: unexpected string= "+ma.group(1));\r
- continue;\r
- }\r
- \r
- // タイトル\r
- {\r
- pdl.title = replaceMarks(pdl, CommonUtils.unEscape(mb.group(4)));\r
- pdl.splitted_title = pdl.title;\r
- }\r
- \r
- \r
- // 番組詳細\r
- {\r
- if (mb.group(9).length() > 0) {\r
- pdl.detail = CommonUtils.unEscape(String.format("%s\n%s", mb.group(9),mb.group(10)));\r
- }\r
- else {\r
- pdl.detail = CommonUtils.unEscape(mb.group(10));\r
- }\r
- \r
- pdl.detail = replaceMarks(pdl, pdl.detail);\r
- \r
- //\r
- pdl.detail = pdl.detail.replaceAll("<a[^>]*?>", "");\r
- pdl.detail = pdl.detail.replaceAll("</a>", "");\r
- \r
- pdl.splitted_detail = pdl.detail;\r
- }\r
- \r
- // 検索用インデックス\r
- pdl.titlePop = TraceProgram.replacePop(pdl.title);\r
- pdl.detailPop = TraceProgram.replacePop(pdl.detail);\r
- pdl.SearchStrKeys = TraceProgram.splitKeys(pdl.titlePop);\r
- \r
- // 詳細へのリンク\r
- pdl.link = "http://program.tv.jp.msn.com"+mb.group(3);\r
- \r
- // 番組長\r
- pdl.length = Integer.valueOf(mb.group(1));\r
- \r
- \r
- // ジャンル\r
- if (mb.group(2).equals(allCode)) {\r
- pdl.genre = ProgGenre.NOGENRE;\r
- }\r
- else if (mb.group(2).equals("anime")) {\r
- pdl.genre = ProgGenre.ANIME;\r
- }\r
- else if (mb.group(2).equals("movie")) {\r
- pdl.genre = ProgGenre.MOVIE;\r
- }\r
- else if (mb.group(2).equals("dorama")) {\r
- pdl.genre = ProgGenre.DORAMA;\r
- }\r
- else if (mb.group(2).equals("sports")) {\r
- pdl.genre = ProgGenre.SPORTS;\r
- }\r
- else {\r
- pdl.genre = ProgGenre.NOGENRE;\r
- }\r
- \r
- // 開始・終了時刻\r
- GregorianCalendar c = new GregorianCalendar();\r
- c.setTime(new Date());\r
- c.set(Calendar.HOUR_OF_DAY, Integer.valueOf(mb.group(5)));\r
- c.set(Calendar.MINUTE, Integer.valueOf(mb.group(6)));\r
- pdl.start = String.format("%02d:%02d", c.get(Calendar.HOUR_OF_DAY), c.get(Calendar.MINUTE));\r
- \r
- c.set(Calendar.HOUR_OF_DAY, Integer.valueOf(mb.group(7)));\r
- c.set(Calendar.MINUTE, Integer.valueOf(mb.group(8)));\r
- pdl.end = String.format("%02d:%02d", c.get(Calendar.HOUR_OF_DAY), c.get(Calendar.MINUTE));\r
- }\r
-\r
- //\r
- int col = -1;\r
- int rowMin = 9999;\r
- for ( int i=0; i<newplist.size(); i++ ) {\r
- ProgList pl = newplist.get(i);\r
- if ( ! (pl.Area.equals(areacode) && pl.SubArea.equals(page)) ) {\r
- continue;\r
- }\r
- if (pl.pdate.get(wdaycol).row < rowMin) {\r
- col = i;\r
- rowMin = pl.pdate.get(wdaycol).row;\r
- }\r
- }\r
- if (col < 0) {\r
- continue;\r
- }\r
- ProgDateList pcl = newplist.get(col).pdate.get(wdaycol);\r
- pcl.pdetail.add(pdl);\r
- pcl.row += pdl.length;\r
- }\r
- }\r
- \r
- private String replaceMarks(ProgDetailList pdl, String text) {\r
- \r
- HashMap<String, Integer> xf = new HashMap<String, Integer>();\r
- \r
- // フラグを拾う\r
- {\r
- String[] marks = new String[] {\r
- "shin", // 新番組\r
- "saisyu", // 最終回\r
- "sai", // 再放送\r
- "hatsu", // 初回放送\r
- "ppv", // [PV]ペイパービュー\r
- "moji", // [字]文字多重放送\r
- "nikakoku", // [二]二か国語放送\r
- "taju", // [多]音声多重放送\r
- "dubbed", // [吹]吹き替え\r
- "data" // [デ]データ放送\r
- // [生]生放送(ないよ)\r
- };\r
- \r
- for (int i=0; i<marks.length; i++) {\r
- String mark = marks[i];\r
- String expr = String.format("/ico_%s\\.gif>", mark);\r
- Matcher mx = Pattern.compile(expr, Pattern.DOTALL).matcher(text);\r
- if ( mx.find() ) {\r
- xf.put(mark, i);\r
- }\r
- }\r
- \r
- for ( String mark : xf.keySet() ) {\r
- String expr = String.format("<img src=http://img.tv.msn.co.jp/s/ico_%s\\.gif>", mark);\r
- text = text.replaceAll(expr,"");\r
- switch (xf.get(mark)) {\r
- case 0:\r
- pdl.flag = ProgFlags.NEW;\r
- break;\r
- case 1:\r
- pdl.flag = ProgFlags.LAST;\r
- break;\r
- case 2:\r
- pdl.addOption(ProgOption.REPEAT);\r
- break;\r
- case 3:\r
- pdl.addOption(ProgOption.FIRST);\r
- break;\r
- case 4:\r
- pdl.addOption(ProgOption.PV);\r
- break;\r
- case 5:\r
- pdl.addOption(ProgOption.SUBTITLE);\r
- break;\r
- case 6:\r
- pdl.addOption(ProgOption.BILINGUAL);\r
- break;\r
- case 7:\r
- pdl.addOption(ProgOption.MULTIVOICE);\r
- break;\r
- case 8:\r
- pdl.addOption(ProgOption.STANDIN);\r
- break;\r
- case 9:\r
- pdl.addOption(ProgOption.DATA);\r
- break;\r
- }\r
- }\r
- }\r
-\r
- // 置換する\r
- {\r
- HashMap<String,String> marks = new HashMap<String,String>();\r
- // 置換するコード\r
- marks.put("director", "[監]");\r
- marks.put("guest", "[ゲ]");\r
- marks.put("jikkyo", "[実]");\r
- marks.put("kaisetsusya", "[解]");\r
- marks.put("katari", "[語]");\r
- marks.put("koe", "[声]");\r
- marks.put("org", "[原]");\r
- marks.put("plot", "[脚]");\r
- marks.put("shikai", "[司]");\r
- marks.put("syutsuen", "[出]");\r
- marks.put("n", "[N]");\r
- marks.put("tenki", "[天]");\r
- marks.put("zen", "(前編)");\r
- marks.put("kou", "(後編)");\r
- // 無視するコード\r
- marks.put("s", "");\r
- marks.put("3d", "");\r
- marks.put("eiga", "");\r
- marks.put("hand", "");\r
- \r
- for ( String mark : marks.keySet() ) {\r
- String expr = String.format("<img src=http://img.tv.msn.co.jp/s/ico_%s\\.gif>", mark);\r
- text = text.replaceAll(expr,marks.get(mark));\r
- }\r
- }\r
- \r
- // 未解析のマーク\r
- Matcher mx = Pattern.compile("/ico_(.*?)\\.gif>", Pattern.DOTALL).matcher(text);\r
- while ( mx.find() ) {\r
- nf.put(mx.group(1), null);\r
- }\r
- for ( String mark : nf.keySet() ) {\r
- String expr = String.format("<img src=http://img.tv.msn.co.jp/s/ico_%s\\.gif>", mark);\r
- text = text.replaceAll(expr,"[?]");\r
- }\r
-\r
- return text;\r
- }\r
- \r
- /*\r
- * ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★\r
- * ★★★★★ 放送地域を取得する(TVAreaから降格)-ここから ★★★★★\r
- * ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★\r
- */\r
-\r
- /*\r
- * 公開メソッド\r
- */\r
- \r
- //\r
- @Override\r
- public String getDefaultArea() {return "東京";}\r
- \r
- //\r
- @Override\r
- public void loadAreaCode(){\r
- \r
- // 設定ファイルが存在していればファイルから\r
- File f = new File(getAreaSelectedFile());\r
- if ( f.exists() ) {\r
- @SuppressWarnings("unchecked")\r
- ArrayList<AreaCode> tmp = (ArrayList<AreaCode>) CommonUtils.readXML(getAreaSelectedFile());\r
- if ( tmp != null ) {\r
- System.out.println("地域リストを読み込みました: "+getAreaSelectedFile());\r
- aclist = tmp;\r
- return;\r
- }\r
-\r
- System.out.println("地域リストの読み込みに失敗しました: "+getAreaSelectedFile());\r
- }\r
- \r
- // 地域一覧の作成\r
- ArrayList<AreaCode> newaclist = new ArrayList<AreaCode>();\r
-\r
- // 存在していなければWeb上から\r
- String response = "";\r
- {\r
- String uri = "http://program.tv.jp.msn.com/tv.php?site=032&mode=06&template=program&category=g&shour=05&lhour=24";\r
- response = webToBuffer(uri, thisEncoding, true);\r
- if ( response == null ) {\r
- System.out.println("地域情報の取得に失敗しました: "+uri);\r
- return;\r
- }\r
- }\r
- \r
- Matcher ma = Pattern.compile("<select name=\"area\" size=\"1\">(.+?)</select>").matcher(response);\r
- if (ma.find()) {\r
- Matcher mb = Pattern.compile("<option value=\"([^\"]+?)\" ?(selected=\"selected\")?>(.+?)</option>").matcher(ma.group(1));\r
- while (mb.find()) {\r
- AreaCode ac = new AreaCode();\r
- ac.setArea(mb.group(3));\r
- ac.setCode(mb.group(1));\r
- newaclist.add(ac);\r
- }\r
- }\r
- \r
- if ( newaclist.size() == 0 ) {\r
- System.err.println(ERRID+"地域一覧の取得結果が0件だったため情報を更新しません");\r
- return;\r
- }\r
- \r
- {\r
- {\r
- AreaCode ac = new AreaCode();\r
- ac.setArea("全国");\r
- ac.setCode(allCode);\r
- newaclist.add(0,ac);\r
- }\r
- {\r
- AreaCode ac = new AreaCode();\r
- ac.setArea("BS");\r
- ac.setCode(bsCode);\r
- newaclist.add(ac);\r
- }\r
- }\r
- \r
- aclist = newaclist;\r
- saveAreaCode();\r
- }\r
-\r
- /*\r
- * ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★\r
- * ★★★★★ 放送地域を取得する(TVAreaから降格)-ここまで ★★★★★\r
- * ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★\r
- */\r
- \r
- \r
- \r
- /*\r
- * ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★\r
- * ★★★★★ 放送局を選択する(TVCenterから降格)-ここから ★★★★★\r
- * ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★\r
- */\r
- \r
- /*\r
- * 公開メソッド\r
- */\r
-\r
- // 設定ファイルがなければWebから取得\r
- @Override\r
- public void loadCenter(String code, boolean force) {\r
- \r
- if ( code == null ) {\r
- System.out.println(ERRID+"地域コードがnullです.");\r
- return;\r
- }\r
- \r
- // BSのページ数の初期化(事前に判明していない場合は2)\r
- int bscntTmp = CommonUtils.loadCnt(getBscntFile());\r
- bscntTmp = bscnt = (bscntTmp > 0)?(bscntTmp):(3);\r
- \r
- //\r
- String centerListFile = getCenterListFile(getTVProgramId(), code);\r
- \r
- if (force) {\r
- File f = new File(centerListFile);\r
- f.delete();\r
- }\r
- \r
- File f = new File(centerListFile);\r
- if (f.exists() == true) {\r
- @SuppressWarnings("unchecked")\r
- ArrayList<Center> tmp = (ArrayList<Center>) CommonUtils.readXML(centerListFile);\r
- if ( tmp != null ) {\r
- \r
- crlist = tmp;\r
- \r
- // 放送局名変換\r
- attachChFilters();\r
- \r
- System.out.println("放送局リストを読み込みました: "+centerListFile);\r
- return;\r
- }\r
-\r
- System.out.println("放送局リストの読み込みに失敗しました: "+centerListFile);\r
- }\r
- \r
- // 放送局をつくるよ\r
- ArrayList<Center> newcrlist = new ArrayList<Center>();\r
- \r
- // 地上派・UHFは地域別に扱う\r
-\r
- int cntMax = ((allCode.equals(code))?(aclist.size()-2):(1))*2 + bscnt;\r
- int cnt = 1;\r
- for (AreaCode ac : aclist) {\r
- if (ac.getCode().equals(bsCode)) {\r
- continue;\r
- }\r
- else if (code.equals(allCode) && ac.getCode().equals(allCode)) {\r
- continue;\r
- }\r
- else if ( ! code.equals(allCode) && ! ac.getCode().equals(code)) {\r
- continue;\r
- }\r
- \r
- String url;\r
- \r
- // 地上波\r
- url = "http://program.tv.jp.msn.com/tv.php?site=032&mode=06&template=program&category=g&area="+ac.getCode()+"&shour=05&lhour=24";\r
- if ( _loadCenter(newcrlist, ac.getCode(), "1", url) ) {\r
- reportProgress(String.format("放送局情報を取得しました[%s%d]: (%d/%d) %s","地上波",cnt,cnt,cntMax,url));\r
- }\r
- cnt++;\r
- \r
- // UHF・BSアナログ\r
- url = "http://program.tv.jp.msn.com/tv.php?site=032&mode=06&template=program&category=s&area="+ac.getCode()+"&shour=05&lhour=24";\r
- if ( _loadCenter(newcrlist, ac.getCode(), "2", url) ) {\r
- reportProgress(String.format("放送局情報を取得しました[%s%d]: (%d/%d) %s","地上波",cnt,cnt,cntMax,url));\r
- }\r
- cnt++;\r
- }\r
- \r
- // BS1・BS2は共通にする(bscntは_loadCenter()中で増加する可能性あり)\r
- \r
- for ( int d=1; d<=bscnt; d++ )\r
- {\r
- String url = "http://program.tv.jp.msn.com/tv.php?site=032&mode=06&template=program&category=d"+d+"&shour=05&lhour=24";\r
- if ( _loadCenter(newcrlist, bsCode, String.valueOf(d), url) ) {\r
- reportProgress(String.format("放送局情報を取得しました[%s%d]: (%d/%d) %s","BS",d,cnt,cntMax,url));\r
- }\r
- cnt++;\r
- }\r
- \r
- // BSのページ数を記録する\r
- \r
- if ( bscntTmp < bscnt ) {\r
- reportProgress(String.format("BSのページ数が変更されました: %d→%d (最大%dまで)",bscntTmp,bscnt,bscntMax));\r
- CommonUtils.saveCnt(bscnt,getBscntFile());\r
- }\r
- \r
- if ( newcrlist.size() == 0 ) {\r
- System.err.println(ERRID+"放送局情報の取得結果が0件だったため情報を更新しません");\r
- return;\r
- }\r
- \r
- crlist = newcrlist;\r
- attachChFilters(); // 放送局名変換\r
- saveCenter();\r
- }\r
- \r
- private boolean _loadCenter(ArrayList<Center> newcrlist, String code, String page, String uri) {\r
- String response = null;\r
- {\r
- response = webToBuffer(uri, thisEncoding, true);\r
- if ( response == null ) {\r
- System.out.println("放送局情報の取得に失敗しました: "+uri);\r
- return false;\r
- }\r
- }\r
- \r
- // BSのページ数を計算する\r
- \r
- for ( int i=bscnt+1; i<=bscntMax; i++ ) {\r
- if ( ! response.matches(".*&category=d"+i+"\".*") ) {\r
- if ( bscnt < i-1 ) {\r
- bscnt = i-1;\r
- }\r
- break;\r
- }\r
- }\r
- \r
- // 局名リストに追加する\r
- \r
- Matcher ma = Pattern.compile("<!-- ↓ Station -->([\\s\\S]+?)<!-- ↑ Station -->").matcher(response);\r
- if (ma.find()) {\r
- Matcher mb = Pattern.compile("<TH width=\".*?\">\\s*(.+?)\\s*</th>").matcher(ma.group(1));\r
- while (mb.find()) {\r
- String centerName;\r
- String link;\r
- Matcher mc = Pattern.compile("<a href =\"(.*?)\" id=\'ch01_h\' class=\"th_a\" target=\"_blank\"\\s*>\\s*(.+?)(<BR>|</a>)").matcher(mb.group(1));\r
- if (mc.find()) {\r
- centerName = CommonUtils.unEscape(mc.group(2));\r
- link = mb.group(1);\r
- }\r
- else {\r
- centerName = CommonUtils.unEscape(mb.group(1).replaceAll("<BR>", ""));\r
- link = "";\r
- }\r
- \r
- // NHK総合・NHK教育\r
- centerName = centerName.replaceFirst("^NHK$", "NHK総合");\r
- centerName = centerName.replaceFirst("^NHK Eテレ", "NHK Eテレ");\r
- if ( ! code.startsWith(bsCode) && page.equals("1")) {\r
- if (centerName.startsWith("NHK")) {\r
- centerName = centerName+"・"+getArea(code);\r
- }\r
- }\r
- \r
- Center cr = new Center();\r
- cr.setAreaCode(code);\r
- cr.setCenterOrig(centerName);\r
- cr.setLink(link);\r
- cr.setType(page);\r
- cr.setEnabled(true);\r
- newcrlist.add(cr);\r
- }\r
- }\r
- return true;\r
- }\r
- \r
- /*\r
- * ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★\r
- * ★★★★★ 放送局を選択する(TVCenterから降格)-ここまで ★★★★★\r
- * ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★\r
- */\r
-}\r