/*
 * PS3 Media Server, for streaming any medias to your PS3.
 * Copyright (C) 2008  A.Brochard
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; version 2
 * of the License only.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */
package net.pms.dlna;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Iterator;  //regzamod
import java.net.URL;;  //regzamod
import java.net.URLDecoder;
import java.util.regex.Pattern;
import java.util.regex.Matcher;
import java.util.Map;
import java.util.HashMap;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.BufferedReader;


import org.apache.commons.lang.StringUtils;
import org.jdom.Content;
import org.jdom.Element;

import com.sun.syndication.feed.synd.SyndCategory;
import com.sun.syndication.feed.synd.SyndEnclosure;
import com.sun.syndication.feed.synd.SyndEntry;
import com.sun.syndication.feed.synd.SyndFeed;
import com.sun.syndication.io.SyndFeedInput;
import com.sun.syndication.io.XmlReader;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.jsoup.Jsoup; //regzamod
//import org.jsoup.nodes.Document; //regzamod
import org.jsoup.nodes.*; //regzamod
import org.jsoup.select.*; //regzamod
import org.jsoup.helper.DataUtil;

import net.pms.formats.Format;
import net.pms.PMS;	//regzamod
import net.pms.util.PMSUtil;	// regzamod
import net.pms.dlna.MapFile;	// regzamod
import net.pms.dlna.VideosFeed;	// regzamod
import net.pms.dlna.WebVideoStream;	// regzamod
import net.pms.newgui.rz_WebTab;
import net.pms.dlna.virtual.VirtualFolder;
import net.pms.configuration.PmsConfiguration;
import net.pms.util.StringUtil;


/**
 * TODO: Change all instance variables to private. For backwards compatibility
 * with external plugin code the variables have all been marked as deprecated
 * instead of changed to private, but this will surely change in the future.
 * When everything has been changed to private, the deprecated note can be
 * removed.
 */
public class Feed extends DLNAResource {
	private static final Logger logger = LoggerFactory.getLogger(Feed.class);
	private static final PmsConfiguration pconf= PMS.getConfiguration();
	private int duration_add=pconf.getRZ_web_duration_add();
	private boolean forced_name;
	/**
	 * @deprecated Use standard getter and setter to access this variable.
	 */
	/*
	@Deprecated
	protected String name;
	*/

	/**
	 * @deprecated Use standard getter and setter to access this variable.
	 */
	@Deprecated
	protected String url;

	/**
	 * @deprecated Use standard getter and setter to access this variable.
	 */
	@Deprecated
	protected String tempItemTitle;
	
	protected long tempSerialId;	//regzamod, Feed item's got_order

	/**
	 * @deprecated Use standard getter and setter to access this variable.
	 */
	@Deprecated
	protected String tempItemLink;

	/**
	 * @deprecated Use standard getter and setter to access this variable.
	 */
	@Deprecated
	protected String tempFeedLink;

	/**
	 * @deprecated Use standard getter and setter to access this variable.
	 */
	@Deprecated
	protected String tempCategory;

	/**
	 * @deprecated Use standard getter and setter to access this variable.
	 */
	@Deprecated
	protected String tempItemThumbURL;
	
	float tempDuration;	//regzamod

	@Override
	public boolean analyzeChildren(int count) {
		//action for feedFolder opened
		if(PMS.rz_trace>0) PMS.trace("Feed.analyzeChildren: called, forced_name="+forced_name);
		if(forced_name) {
			resolve_in();
			return true;
		}
		return true;
	}

	@Override
	public void resolve() {
		if(PMS.rz_trace>0) PMS.trace("Feed.resolve: called, forced_name="+forced_name);
		//action for disp feedFolder as a list entry,
		//when parent folder is opened
		if(forced_name) {
			//my name is given, so needn't resolve(read feed content & create feedlist) yet
			return;
		}
		//my name is not given, so must resolve(read feed content & create feedlist) to get my name
		resolve_in();
		
	}
	

	private void resolve_in() {
		//never called after initial 
		//---- regzamod add start
		if(PMS.rz_trace>0) PMS.trace("Feed.resolve_in: start, resolved="+resolved);
		
		if(resolved) {	
			if(PMS.rz_trace>0) PMS.trace("Feed.resolve: alreday resolved");
			return;
		}
		else {
			if(PMS.rz_trace>0) PMS.trace("Feed.resolve: not resolved yet --> exec resolve");
		}
		//---- regzamod add end
		//resolved=true;

		//Once, delete all children, othewise old children remain !!		
		//getChildren().clear();	// regzamod
		ClearChildren();
		deleteMenus();
		
		super.resolve();
		try {
			//PMS.dbg("Feed.resolve: call parse");
			int rc=parse();
			if(sort_enabled && pconf.isRZ_sort_when_refresh()) {	//regzamod
				//PMS.dbg("Feed.resolve: set doSort=true, sort_type="+getSortType());
				doSort=true;
				//MapFile.dlnaListSort(getChildren(),getSortType());
			}
			if(rc<=0) {
				logger.warn("Feed.resolve: parsed entry count is null, url="+url);
				logger.warn("--> to Retry, once Clear Media Cache");
			}
			resolved=true;
		} catch (Exception e) {
			if(PMS.rz_debug>1) {
				logger.error("Feed.resolve: Error in parsing stream=" + url, e);
			}
			else {
				logger.error("Feed.resolve: Error in parsing stream=" + url+", \nErrorMsg="+e.getMessage());
			}
			resolved=false;
		}
		
		//---- crete menues
		//Menu will be not created if no entry created (retrieve failed)
		//force create minimum menu (for retry retrieve, etc)
		if(ctlFolder==null) {
			int ures=(isUnderResumeFolder(this)?1:0);
			//createSortMenu(this,ures);
			createResumeFolder(this,ures);
			createClearFolderCahceMenu(this,ures);
			createScriptMenuFolder(this,ures);
			createSelClipPath(this,ures);
			//createKeyExtructMenu(this,ures);
			createVideoSettingMenu(this);
		}
		else {
			createVideoSettingMenu(this);
		}
		
		//---- menu check
		if(PMS.rz_debug>2) {
			DLNAResource ctl=null;
			for(DLNAResource ch : getChildren()) {
				PMS.dbg("Feed.resolve_in: child="+ch.getName());
				if(ch==ctlFolder) {
					ctl=ch;
					PMS.dbg("Feed.resolve_in: ctlFolder found="+ch);
					break;
				}
			}
			if(ctl!=null) {
				PMS.dbg("Feed.resolve_in: ctlFolder childrens are ....");
				for(DLNAResource ch : ctl.getChildren()) {
					PMS.dbg("Feed.resolve_in: ctlFolder child="+ch.getName());
				}
				
				PMS.dbg("Feed.resolve_in: menu children are .... "
					+"\n  ctlFolder="			+ctl
					+"\n  sortMenu="			+ctl.sortMenu
					+"\n  transFolder="			+ctl.transFolder
					+"\n  resumeFolder="		+ctl.resumeFolder
					+"\n  resumeFolder2="		+ctl.resumeFolder2
					+"\n  ClearFolderCahceMenu="+ctl.ClearFolderCahceMenu
					+"\n  ScriptMenuFolder="	+ctl.ScriptMenuFolder
				);
			}
		}
		
		//---- kick resolve thread for children
		if(false) {  //too heavy
			Runnable r = new Runnable() {
				@Override
				public void run() {
					resolve_ch();
				}
			}; 
			Thread t = new Thread(r);
			t.start();
		}
	}

	private void resolve_ch() {
		//PMS.dbg("Feed.resolve_ch: start resolve children");
		for(DLNAResource ch :getChildren()){
			if(ch.sotype==DLNAResource.SO_VIDEO_STREAM) {
				((WebVideoStream)ch).resolve_s();
			}
		}
		//PMS.dbg("Feed.resolve_ch: start resolve children");
	}

	public void setResolved(boolean set) {	//regzamod add
		//PMS.dbg("Feed: serResolved="+set);
		resolved=set;
	}

	public Feed(String name, String url, int type) {
		super(type);
		setUrl(url);
		setName(name);
		if(name!=null) forced_name=true;  //no presetted name
		this.mflags |=DLNAResource.MF_WEB_FOLDER;
	}

	@Override
	public int getSortType() {	//regzamod add
		if(rz_MetaSortType>=0) {
			return rz_MetaSortType;
		}
		return PMS.rz_web_sort_type;
	}

	public int parse() throws Exception {
		if(url==null || url.length()<=0) {
			if(PMS.rz_debug>1) PMS.dbg("Feed.parse: url is null --> noop");
			return -1;
		}
		String pval[]=getPval(url);  //get multi-page tag buried in url
		int cnt=-1;
		int cntw=0;
		if(pval!=null) {
			//exec mult-page seach
			for(int i=1; i<pval.length; i++) {
				String url0=url.replace(pval[0],pval[i]);
				//PMS.dbg("Feed.parse: exec multi-page-search No"+i+", url="+url0);
				cntw=parse_feed(url0);
				if(cntw<=0) break; //no more results
				cnt+=cntw;
			}
		}
		else {
			cnt=parse_feed(url);
		}
		return cnt;
	}
	
	//regzamod, get multi-page tag buried in url
	private String[] getPval(String url) {
		String tagtop="{$pval=";
		List<String> pval=  new ArrayList<String>();
		int pos=url.indexOf(tagtop);
		if(pos>0) {
			String tag=url.substring(pos);
			pos=tag.indexOf("}");
			if(pos>0) {
				tag=tag.substring(0,pos+1);
				pval.add(tag);
				String params=tag.substring(tagtop.length(),tag.length()-1);
				//PMS.dbg("getPval: got tag="+tag+", params="+params);
				String[] mpara=params.split(";");
				for(String s: mpara) {
					s=s.trim();
					if(s.length()>0)
						pval.add(s);
				}
			}
		}
		if(pval.size()>1) {
			return (String[])pval.toArray(new String[0]);
		}
		else return null;
	}
	
	@SuppressWarnings("unchecked")
	public int parse_feed (String url) throws Exception {
		//PMS.dbg("Feed.parse_feed: called url="+url);
		int cnt=0;
		int parse_type=0;
		
		if(url.startsWith("file:") &&(url.endsWith(".html")||url.endsWith(".htm"))) {
			parse_type=2;
		}
		else if(url.contains("https://www.google.co.jp/")) {
			parse_type=2;
		}
		else if(url.contains("https://www.youtube.com/user/")) {
			parse_type=2;
		}
		else if(url.contains("https://www.youtube.com/channel/")) {
			parse_type=2;
		}
		else if(url.contains("://www.youtube.com/playlist?list=")) {
			//parse_type=3;   //old: use inner parser (parse_html_ytpls())
			parse_type=4;	//new: use external universal parser
		}
		else {
			//parse_type=4;
		}
		if(parse_type==0) {
			if(PMS.rz_debug>1) PMS.dbg("parse_feed: parse_type="+parse_type+", call parse_html_rss, url="+url);
			cnt=parse_html_rss(url);  //original rss parser
		}
		else if(parse_type==2) {  //results format is general html(google search), not RSS
			if(getSpecificType()==Format.VIDEO) {
				if(PMS.rz_debug>1) PMS.dbg("parse_feed: parse_type="+parse_type+", call parse_html_google_video, url="+url);
				cnt=parse_html_google_video(url);
			}
			else {
				if(PMS.rz_debug>1) PMS.dbg("parse_feed: parse_type="+parse_type+", call parse_html_google_img, url="+url);
				cnt=parse_html_google_img(url);
			}
			return cnt;
		}
		else if(parse_type==3) {
			if(PMS.rz_debug>1) PMS.dbg("parse_feed: parse_type="+parse_type+", call parse_html_ytpls, url="+url);
			cnt=parse_html_ytpls(url);  //youtube playlist
			return cnt;
		}
		else if(parse_type==4) {
			if(PMS.rz_debug>1) PMS.dbg("parse_feed: parse_type="+parse_type+", call parse_html_rx, url="+url);
			cnt=parse_html_rx(url);  //universal parser
			return cnt;
		}
		setLastmodified(System.currentTimeMillis());
		return cnt;
	}
	
	//------------------------------------------------------------------------------
	// Parse using external html paser
	//------------------------------------------------------------------------------
	@SuppressWarnings("unchecked")
	public int parse_html_rx (String url) throws Exception {
		if(PMS.rz_debug>0) PMS.dbg("parse_html_rx: Start url="+url);
		
		int cnt=0;
		SyndFeedInput input = new SyndFeedInput();
		
		//byte b[] = downloadAndSendBinary(url);
		byte b[] = kickExtParser(url);

		if (b != null) {
			String content = new String(b,"UTF-8");
			content = stripNonValidXMLCharacters(content);
			if(PMS.rz_debug>1) PMS.dbg("parse_html_rx: content="+content);
			
			SyndFeed feed = input.build(new XmlReader(new ByteArrayInputStream(b)));
			String name=feed.getTitle();
			if(name==null) name="Feeds without Title";	//regzamod
			if(!forced_name) setName(name);
			if (feed.getCategories() != null && feed.getCategories().size() > 0) {
				SyndCategory category = (SyndCategory) feed.getCategories().get(0);
				setTempCategory(category.getName());
			}
			List<SyndEntry> entries = feed.getEntries();
			long	i=10;	//regzamod
			setTempFeedLink(null);  //regzam
			for (SyndEntry entry : entries) {
				i++;
				setTempItemTitle(entry.getTitle());
				setTempItemLink(entry.getLink());
				setTempItemThumbURL(null);
				tempDuration=0;	//regzamod

				ArrayList<Element> elements = (ArrayList<Element>) entry.getForeignMarkup();
				for (Element elt : elements) {
					if ("group".equals(elt.getName()) && "media".equals(elt.getNamespacePrefix())) {
						List<Content> subElts = elt.getContent();
						for (Content subelt : subElts) {
							if (subelt instanceof Element) {
								parseElement((Element) subelt, false); //origin 
							}
						}
					}
					parseElement(elt, true);
				}
				List<SyndEnclosure> enclosures = entry.getEnclosures();
				for (SyndEnclosure enc : enclosures) {
					if (StringUtils.isNotBlank(enc.getUrl())) {
						setTempItemLink(enc.getUrl());
					}
				}
				setTempSerialId(i++); 
				cnt++;
				manageItem();
			}
		}
		setLastmodified(System.currentTimeMillis());
		return cnt;
	}
	
	private Process p_parse;
	
	private byte[] kickExtParser (String url) {
		String python=pconf.getRZ_python_path();
		String parser="regzamod/rzUtility/HtmlParse.py";
		ByteArrayOutputStream bytes = new ByteArrayOutputStream();
		
		//---- setup params for the process
		List<String> args = new ArrayList<String>();
		args.add(python);
		args.add(parser);
		args.add(url);
		args.add("-");
		
		//---- kick parser
		ProcessBuilder pb = new ProcessBuilder(args);
		try {
			p_parse = pb.start();
		} catch (Exception e) {
			logger.error("kickExtParser: pb.start(): Exception error=%s",e.getMessage());
			return null;
		}

		//---- thread for read stderr of parser
		Runnable read_stderr = new Runnable() {
			@Override
			public void run() {
				InputStream ie = p_parse.getErrorStream();
				BufferedReader br = new BufferedReader(new InputStreamReader(ie));
				String line;
				try {
					while(true) {
						line= br.readLine();
						if(line == null) break;
					}
					br.close();
				} catch (IOException e) {
					logger.error("kickExtParser: read stderr from ext_parser: IOException error="+e.getMessage());
				} 
			}
		};
		new Thread(read_stderr).start();

		//---- read stdout of parser
		InputStream in = p_parse.getInputStream();
		byte buf[] = new byte[4096];
		int n = -1;
		try {
		while ((n = in.read(buf)) > -1) {
			bytes.write(buf, 0, n);
		}
		in.close();
		} catch (IOException e) {
			logger.error("kickExtParser: read stdout from ext_parser: IOException error="+e.getMessage());
		}
		return bytes.toByteArray();
	}

	@SuppressWarnings("unchecked")
	public int parse_html_rss (String url) throws Exception {
		if(PMS.rz_debug>1) PMS.dbg("parse_html_rss (original): Start url="+url);
		
		int cnt=0;
		SyndFeedInput input = new SyndFeedInput();
		byte b[] = downloadAndSendBinary(url);
		if (b != null) {
			String content = new String(b, "UTF-8");
			content = stripNonValidXMLCharacters(content);
			if(PMS.rz_debug>1) PMS.dbg("parse_html_rss: content="+content);
			SyndFeed feed = input.build(new XmlReader(new ByteArrayInputStream(b)));
			String name=feed.getTitle();
			if(name==null) name="Feeds without Title";	//regzamod
			if(!forced_name) setName(name);
			if (feed.getCategories() != null && feed.getCategories().size() > 0) {
				SyndCategory category = (SyndCategory) feed.getCategories().get(0);
				setTempCategory(category.getName());
			}
			List<SyndEntry> entries = feed.getEntries();
			long	i=10;//regzamod
			setTempFeedLink(null);  //regzam
			for (SyndEntry entry : entries) {
				i++;
				if(PMS.rz_debug>10) PMS.dbg("parseElement: entry No"+i+" ="+entry);
				setTempItemTitle(entry.getTitle());
				setTempItemLink(entry.getLink());
				//setTempFeedLink(entry.getUri());
				setTempItemThumbURL(null);
				tempDuration=0;//regzamod

				ArrayList<Element> elements = (ArrayList<Element>) entry.getForeignMarkup();
				for (Element elt : elements) {
					if ("group".equals(elt.getName()) && "media".equals(elt.getNamespacePrefix())) {
						List<Content> subElts = elt.getContent();
						for (Content subelt : subElts) {
							if (subelt instanceof Element) {
								parseElement((Element) subelt, false); //origin 
							}
						}
					}
					parseElement(elt, true);
				}
				List<SyndEnclosure> enclosures = entry.getEnclosures();
				for (SyndEnclosure enc : enclosures) {
					if (StringUtils.isNotBlank(enc.getUrl())) {
						setTempItemLink(enc.getUrl());
					}
				}
				setTempSerialId(i++); //regzamod
				cnt++;
				manageItem();
			}
		}
		setLastmodified(System.currentTimeMillis());
		return cnt;
	}

	public int parse_html_google_img (String url) throws Exception {
		String agent="Firefox/26.0";
		//String agent="Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.4; en-US; rv:1.9.2.2) Gecko/20100316 Firefox/3.6.2";
		Document doc=null;
		
		if(PMS.rz_debug>1) PMS.dbg("parse_html_google_img: Start url="+url);
		
		/* image entry sample
		<div class="rg_di rg_el ivg-i" 
			data-ved="0ahUKEwiggfWDiYHMAhUBHJQKHXXjB08QMwgjKAAwAA">
			<a jsaction="fire.ivg_o;mouseover:str.hmov;mouseout:str.hmou" class="rg_l">
			<img data-sz="f" name="C1MbNDCkb-NOIM:" class="rg_i" alt="「landscape」の画像検索結果" 
			jsaction="load:str.tbn" onload="google.aft&&google.aft(this)">
			<div class="_aOd rg_ilm">
				<div class="rg_ilmbg"><span class="rg_ilmn"> 1920&#215;1080 - science-all.com </span></div>
			</div></a>
			<div class="rg_meta">
				{"cb":3,"id":"C1MbNDCkb-NOIM:","isu":"science-all.com","itg":false,"ity":"jpg","oh":1080,
				"ou":"http://science-all.com/images/landscape/landscape-04.jpg","ow":1920,"pt":"Landscape Dream Interpretation",
				"rid":"NpjAvRmQ8ZAfJM","ru":"http://science-all.com/landscape.html","s":"","th":168,
				"tu":"https://encrypted-tbn3.gstatic.com/images?q\u003dtbn:ANd9GcTDpl-shLvs0Oq8A9TwSmJdu8HFW_ud2jflntiCKq4-P7uLcM0E",
				"tw":300}
			</div>
		</div>
		<!--n--><!--m-->
		*/
		
		
		if(false) {
			byte b[] = downloadAndSendBinary(url);
			String content = new String(b, "UTF-8");
			//content = stripNonValidXMLCharacters(content);
			//PMS.dbg("Feed.parse_html: called url="+url);
			//PMS.dbg("Feed.parse_html: body="+content);
		}
		
		try {
		    if(url.startsWith("file:")) {
		    	url=url.substring("file:".length());
			    File f= new File(url);
			    doc = DataUtil.load(f,"UTF-8",url);
		    }
		    else {
		    	doc = Jsoup.connect(url).header("User-Agent",agent).get();
		    }
		} catch (IOException e) {
			if(PMS.rz_debug>1) {
				logger.error("Feed.parse_html_google_img: Error in getFeed url=" + url, e);
			}
			else {
				logger.warn("Feed.parse_html_google_img: Error in getFeed url==" + url+", \nErrorMsg="+e.getMessage());
			}
		    //e.printStackTrace();
		}		
		if (doc == null) {
			return -1;
		}
		
		//---- thumbnail data in script
		//,["HWwY1OCi5-ED-M:","data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD...bRdiR409/nF/3f6j9YHKsiQ\u003d\u003d"]
		//,["MP9EPqpMBMzbQM:","data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD...lFEUIBI3S+dVFFVeB14F+KhBUUQB//2Q\u003d\u003d"]

		Map<String, String> thumbtb = new HashMap<String, String>();
		Elements scripts = doc.select("script");  //get all scripts
		Iterator<org.jsoup.nodes.Element> scs=scripts.iterator();
		String ptn_thumbdt="\\[\"(.*):\",\"(data:image/.+;base64,.+)\"\\]";
		Pattern p1= Pattern.compile(ptn_thumbdt); 
			
		Matcher m;
		while(scs.hasNext()) {
			org.jsoup.nodes.Element sc=scs.next();
			//if(PMS.rz_debug>1) PMS.dbg("parse_html_google_img: Script found="+sc.html());
			m = p1.matcher(sc.html());
			while(m.find()) {
				if(PMS.rz_debug>1) PMS.dbg("parse_html_google_img: image_body found groupCount="+m.groupCount());
				if(m.groupCount()>=2) { // group count in pattern 
					if(PMS.rz_debug>1) PMS.dbg("parse_html_google_imag: image_body found key="+m.group(1)+", data="+m.group(2));
					thumbtb.put(m.group(1),m.group(2));
				}
			}
		}
		//----

		Elements title=doc.getElementsByTag("title");	
		//Elements imgs=doc.select("a[href*=imgurl]");	//a href=x, x Contains "imgurl"
		//Elements imgs=doc.select("div[class^=rg_di rg_el ivg-i]");  //div class=x, x starsWith "rg_di rg_el ivg-i"
		Elements imgs=doc.select("div[class^=rg_di");  //div class=x, x starsWith "rg_dii"
		Iterator<org.jsoup.nodes.Element> ei=imgs.iterator();
		String str,strw,imgurl=null,img_thumb_id,thumbdt=null;
		String sc,sc1,sc2;
		int pos;
		long i=10;
		int cnt=0;
		
		sc1="&quot;ou&quot;:&quot;"; 	// "ou":" imgurl
		sc2="&quot;id&quot;:&quot;";  	// "id":" thumbnail id
		
		while(ei.hasNext()) {
			imgurl=null;
			img_thumb_id=null;
			str=ei.next().toString();
			//PMS.dbg("parse_html_google_img: found matched element No."+cnt+", str="+str);
			
			//---- image body url
			//sample: "ou":"http://science-all.com/images/landscape/landscape-04.jpg"
			pos=str.indexOf(sc1);
			if(pos>-1) {
				strw=str.substring(pos+sc1.length());
				//pos=str.indexOf("&amp;");
				sc="&quot;";  // tail of "ou":"http://science-all.com/images/landscape/landscape-04.jpg",
				pos=strw.indexOf(sc);
				if(pos>-1) {
					imgurl=strw.substring(0,pos);
				}
				//PMS.dbg("parse_html_google_img: found imgurl="+imgurl);
			}
			//---- image thumbnail
			// sample "id":"C1MbNDCkb-NOIM:"
			pos=str.indexOf(sc2);
			if(pos>-1) {
				strw=str.substring(pos+sc2.length());
				sc=":&quot;";  // tail of "id":"C1MbNDCkb-NOIM:"
				pos=strw.indexOf(sc);
				if(pos>-1) {
					img_thumb_id=strw.substring(0,pos);
					if(img_thumb_id!=null) {
						thumbdt=thumbtb.get(img_thumb_id); //old type?
						//thumbdt=img.attr("src");
						if(PMS.rz_debug>1) PMS.dbg("parse_html_google_img: thumb found="+thumbdt);
					}
					//PMS.dbg("parse_html_google_img: found img_thumb_id="+img_thumb_id
					//	+", thumbdt="+(thumbdt==null?"null":thumbdt.substring(0,50)));
				}
			}
			
			if(imgurl!=null) {
				i++;
				cnt++;
				try {
					//String uname=URL.getPath();  //can't get name part
					String uname=imgurl;
					int pos1=uname.lastIndexOf("/");
					if(pos1>0) {
						uname=imgurl.substring(pos1+1);
					}
					uname=uname.replaceAll("%2525", "%"); //for fool picasa url
					uname=URLDecoder.decode(uname,"UTF-8");
					
					setTempItemTitle(uname);
				} catch (IOException e) {
					setTempItemTitle("image"+(i-10));
				}
				setTempItemLink(imgurl);
				setTempFeedLink(null);
				setTempItemThumbURL(thumbdt);
				tempDuration=0;//regzamod
				setTempSerialId(i); //regzamod
				manageItem();
				//PMS.dbg("imgurl="+imgurl);
			}
		}
		if(!forced_name) {
			//forced_name=false;
			if(title==null) 
				setName("Search Results");
			else 
				setName(title.text());
		}
		setLastmodified(System.currentTimeMillis());
		return cnt;
	}

	public int parse_html_img_old(String url) throws Exception {
		Document doc=null;
		//String agent="Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.4; en-US; rv:1.9.2.2) Gecko/20100316 Firefox/3.6.2";
		String agent="Firefox/26.0";
		
		/* image entry sample
		*/

		if(false) {
			byte b[] = downloadAndSendBinary(url);
			String content = new String(b, "UTF-8");
			//content = stripNonValidXMLCharacters(content);
			//PMS.dbg("Feed.parse_html: called url="+url);
			//PMS.dbg("Feed.parse_html: body="+content);
		}
		
		try {
		    if(url.startsWith("file:")) {
		    	url=url.substring("file:".length());
			    File f= new File(url);
			    doc = DataUtil.load(f,"UTF-8",url);
		    }
		    else {
		    	doc = Jsoup.connect(url).header("User-Agent",agent).get();
		    }
		} catch (IOException e) {
			if(PMS.rz_debug>1) {
				logger.error("Feed.parse_html_img: Error in getFeed url=" + url, e);
			}
			else {
				logger.warn("Feed.parse_html_img: Error in getFeed url==" + url+", \nErrorMsg="+e.getMessage());
			}
		    //e.printStackTrace();
		}		
		if (doc == null) {
			return -1;
		}
		/*
		//PMS.dbg("Feed.parse_html: getElementsByTag title="+title.text());
		//Elements body=doc.getElementsByTag("body");
		//PMS.dbg("Feed.parse_html: getElementsByTag body="+body.text());
		//Elements imgs=doc.getElementsByTag("a");
		//Elements imgs=doc.getElementsByAttributeValueContaining("href","imgurl");
		//Elements imgs=doc.select("a[href]");
		*/
		Elements title=doc.getElementsByTag("title");
		Elements imgs=doc.select("a[href*=imgurl]");
		Iterator<org.jsoup.nodes.Element> ei=imgs.iterator();
		String str,imgurl=null;
		int pos;
		long i=10;
		int cnt=0;
		while(ei.hasNext()) {
			imgurl=null;
			str=ei.next().toString();
			pos=str.indexOf("imgurl=");
			if(pos>-1) {
				str=str.substring(pos+"imgurl=".length());
				pos=str.indexOf("&amp;");
				if(pos>-1) {
					imgurl=str.substring(0,pos);
				}
			}
			if(imgurl!=null) {
				i++;
				cnt++;
				try {
					//String uname=URL.getPath();
					String uname=URLDecoder.decode(imgurl,"UTF-8");
					int pos1=uname.lastIndexOf("/");
					if(pos1>0) {
						uname=uname.substring(pos1+1);
					}
					setTempItemTitle(uname);
				} catch (IOException e) {
					setTempItemTitle("image"+(i-10));
				}
				setTempItemLink(imgurl);
				setTempFeedLink(null);
				setTempItemThumbURL(null);
				tempDuration=0;//regzamod
				setTempSerialId(i); //regzamod
				manageItem();
				//PMS.dbg("imgurl="+imgurl);
			}
		}
		if(!forced_name) {
			//forced_name=false;
			if(title==null) 
				setName("Search Results");
			else 
				setName(title.text());
		}
		setLastmodified(System.currentTimeMillis());
		return cnt;
	}
	
	public int parse_html_google_video (String url) throws Exception {
		//currentry supports only for google search + youtube
		Document doc=null;
		String agent="Firefox/26.0";
		
		//default search patterns
		//regular exp. special character : . ^ $ [] * + ? | ()
		ArrayList<String> ptn_grp=new ArrayList<>(Arrays.asList("div[class=g]"));
		ArrayList<String> ptn_url=new ArrayList<>(Arrays.asList("a[href^=http://]","a[href^=https://]"));
		String ptn_thumbid="img[id^=vidthumb]";
		String ptn_thumbdt="\\[\"(vidthumb[0-9]+)\",\"(data:image/.+;base64,.+)\"\\]";
		
		if(PMS.rz_debug>1) PMS.dbg("==== parse_html_google_video: Start url="+url);

		if(false) {
			byte b[] = downloadAndSendBinary(url);
			String content = new String(b, "UTF-8");
		}
		rz_WebTab wt=PMS.get().getRzw();
		if(wt!=null) {
			if(wt.gglvsch_ptn_grp!=null) ptn_grp=wt.gglvsch_ptn_grp;
			if(wt.gglvsch_ptn_url!=null) ptn_url=wt.gglvsch_ptn_url;
			if(wt.gglvsch_ptn_thumbid!=null) ptn_thumbid=wt.gglvsch_ptn_thumbid;
			if(wt.gglvsch_ptn_thumbdt!=null) ptn_thumbdt=wt.gglvsch_ptn_thumbdt;
		}
		
		try {
		    if(url.startsWith("file:")) {
		    	url=url.substring("file:".length());
			    File f= new File(url);
			    doc = DataUtil.load(f,"UTF-8",url);
		    }
		    else {
		    	doc = Jsoup.connect(url).header("User-Agent",agent).get();
		    	//doc = Jsoup.connect(url).userAgent("Mozilla/5.0 Chrome/26.0.1410.64 Safari/537.31")
				//	.timeout(2*1000)
  				//	.followRedirects(true).get();
		    }
		} catch (IOException e) {
			if(PMS.rz_debug>1) {
				logger.error("Feed.parse_html_google_video: Error in getFeed url=" + url, e);
			}
			else {
				logger.warn("Feed.parse_html_google_video: Error in getFeed url==" + url+", \nErrorMsg="+e.getMessage());
			}
			/*
			if(PMS.rz_debug>1) {  //regzam2
		    	e.printStackTrace();
			}
			*/
		}		
		if (doc == null) {
			if(PMS.rz_debug>1) PMS.dbg("parse_html_google_video: search results=null");
			return -1;
		}
		
		if(PMS.rz_debug>1) PMS.dbg("==== parse_html_google_video: Feed retrieve end");
		if(PMS.rz_debug>2) PMS.dbg("==== parse_html_google_video: Retrieved Contents is ..."+doc.html());
		
		Elements title=doc.getElementsByTag("title");
		Elements vids0=null,vids1=null,vids2=null,vids3=null,vids=null;
		int i=0;
		
		if(PMS.rz_debug>1) PMS.dbg("==== parse_html_google_video: group parse Start");
		if(true) {
			if(ptn_grp!=null && ptn_grp.size()>0) {  //OR select
				if(vids2==null) {
					vids2=new Elements();
				}
				for(String s: ptn_grp) {
					//PMS.dbg("parse_html_google_video: gglvsch_ptn2="+s);
					vids1=doc.select(s);
					if(vids1!=null) {
						vids2.addAll(vids1);
					}
				}
				vids=vids2;
			}
		}
		else { //fallback to defaults
			if(true) {
				vids=doc.select("div[class=g");
				vids=vids.select("a[href*=http://");
			}
			else {
				vids1=doc.select("a[href*=http://www.youtube.com/watch?v]");  //ToDo: select by more refined way
				vids2=doc.select("a[href*=http://www.nicovideo.jp/watch/]");  //ToDo: select by more refined way
				vids3=doc.select("a[href*=http://www.dailymotion.com/]");  //ToDo: select by more refined way
				
				vids=new Elements();
				vids.addAll(vids1);
				vids.addAll(vids2);
				vids.addAll(vids3);
			}
		}
		if(PMS.rz_debug>1) PMS.dbg("==== parse_html_google_video: group parse End");
		if(PMS.rz_debug>2) PMS.dbg("==== parse_html_google_video: Results="+vids);
		
		//------------------------------------------------------------------------
		// Results html Sample
		//------------------------------------------------------------------------
		/*
		<div class="g">
			<!--m-->
			<div class="rc" data-hveid="36">
				<h3 class="r">
					<a href="https://www.youtube.com/watch?v=MzvXWwp25Qk" onmousedown="return rwt(this,'','','','1','AFQjCNGPVHc64ImApbnwZA7xM3v5Vbr_HA','','0ahUKEwidjuLS9NPLAhXGl5QKHVcFB2UQtwIIJTAA','','',event)">E-girls / DANCE WITH ME NOW! - Duration - YouTube</a>
				</h3>
				<div class="s">
					<div>
						<div class="th _lyb _YQd" style="height:65px;width:116px">
							<a href="https://www.youtube.com/watch?v=MzvXWwp25Qk" data-ved="0ahUKEwidjuLS9NPLAhXGl5QKHVcFB2UQuAIIJjAA" 
								onmousedown="return rwt(this,'','','','1','AFQjCNGPVHc64ImApbnwZA7xM3v5Vbr_HA','','0ahUKEwidjuLS9NPLAhXGl5QKHVcFB2UQuAIIJjAA','','',event)">
								<g-img class="_ygd">
									<img id="vidthumb1" src="data:image/gif;base64,R0lGODlhAQABAIAAAP///////yH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" 
									class="_WCg" height="87" style="top:-11px" width="116" alt='"E-Girls" の動画検索結果' 
									onload="google.aft&&google.aft(this)">
								</g-img>
								<span class="vdur _dwc">&#9654;&nbsp;4:52</span>
							</a>
						</div>
					</div>
					<div style="margin-left:125px">
						<div class="f kv _SWb" style="white-space:nowrap">
							<cite class="_Rm">https://www.youtube.com/watch?v=MzvXWwp25Qk</cite>
						</div>
						<div class="f slp">2015/12/31 - アップロード元: avex
						</div>
						<span class="st">http://<em>e-girls</em>-ldh.jp/ 2016/2/10リリース <em>E-girls</em>初のベストアルバム『E.G. SMILE -<em>E-</em><wbr><em>girls</em> BEST ...</span>
					</div>
					<div style="clear:left">
					</div>
				</div>
			</div><!--n-->
		</div>
		
		<div class="g">
			<!--m-->
		*/
		//--------------------------------------------------------------------------
		// parse scripts (gather thumb data body)
		//--------------------------------------------------------------------------
		//------------------------------------------------------------------------
		// sample OLD
		//------------------------------------------------------------------------
		// var _image_ids=['uid_1'];_setImagesSrc(_image_ids,_image_src);})();(function(){
		// var _image_src='data:image/jpeg;base64,/9j/4AAQSk...  .......rKylGP/2Q\75\075';
		//------------------------------------------------------------------------
		// sample NEW
		//------------------------------------------------------------------------
		// ["vidthumb21","data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD .....RpJL/2Q\u003d\u003d"],

		Map<String, String> thumbtb = new HashMap<String, String>();
		//Element script = doc.select("script").first(); // get 1st script part
		Elements scripts = doc.select("script");  //get all scripts
		Iterator<org.jsoup.nodes.Element> scs=scripts.iterator();
		
		Pattern p1= Pattern.compile(ptn_thumbdt); 
			
		if(PMS.rz_debug>1) PMS.dbg("==== parse_html_google_video: Searching image_data body in scripts ptn="+ptn_thumbdt);
		Matcher m;
		while(scs.hasNext()) {
			org.jsoup.nodes.Element sc=scs.next();
			if(PMS.rz_debug>1) PMS.dbg("parse_html_google_video: Script found="+sc.html());
			m = p1.matcher(sc.html());
			while(m.find()) {
				if(PMS.rz_debug>1) PMS.dbg("parse_html_google_video: image_entry found Count="+m.groupCount());
				if(m.groupCount()>=2) { // group count in pattern 
					//if(PMS.rz_debug>1) PMS.dbg("parse_html_google_video: image_body found key="+m.group(1)+", data="+m.group(2));
					thumbtb.put(m.group(1),m.group(2));
				}
			}
		}
		
		if(ptn_url==null || ptn_url.size()<1) {
			if(PMS.rz_debug>1) PMS.dbg("parse_html_google_video: ptn_url not defined");
			return 0;
		}
		//--------------------------------------------------------------------------
		// parse tags
		//--------------------------------------------------------------------------
		if(PMS.rz_debug>1) PMS.dbg("==== parse_html_google_video: parse link_tags Start");
			
		Iterator<org.jsoup.nodes.Element> ei0=vids.iterator();
		String str,vidurl=null,vidname,vidimg,thumbdt,prevurl;
		DLNAResource prev_obj;
		org.jsoup.nodes.Element e0;
		int do_sort= -1;
		String channel_id=null;
		String plistid_upld=null;
		VirtualFolder rFolder=null;

		i=10;
		int cnt=0;
		int g=0;
		
		//---- Per Video entry
		while(ei0.hasNext()) {
			e0=ei0.next();
			g++;
			if(PMS.rz_debug>1) {
				PMS.dbg("==== parse_html_google_video: group Start No="+g+ ", Contents="+e0);
			}
			prevurl=null;
			vidurl=null;
			
			vids=new Elements();
			for(String s: ptn_url) {
				vids.addAll(e0.select(s));
			}
			
			//---- Per Tag<a href.. > in a Video entry
			Iterator<org.jsoup.nodes.Element> ei=vids.iterator();
			prevurl=null;
			prev_obj=null;
			vidname=null;
			vidimg=null;
			thumbdt=null;
			channel_id=null;
			setTempFeedLink(null);
			while(ei.hasNext()) {
				vidurl=null;
				org.jsoup.nodes.Element vid=ei.next();
				if(PMS.rz_debug>1) { 
					PMS.dbg("parse_html_google_video: tagname="+vid.tagName()+", tag ="+vid);
				}
				if(vid.tagName().equals("meta")) {
					//<meta itemprop="channelId" content="UCd0kEhvwyIF2QVi1xLu0VxA">
					channel_id=vid.attr("content");
					plistid_upld="UU"+channel_id.substring(2);
					if(PMS.rz_debug>1) PMS.dbg("found meta channel_id="+channel_id); 
				}
				else if(vid.tagName().equals("span")) {
					//<span class="accessible-description" id="description-id-687054">視聴済みです。 - 長さ: 74 秒。</span>
					//<span class="accessible-description" id="description-id-661044"> - 長さ: 4 分、53 秒。</span>
					String str_dur=vid.text();
					String str_hour="0",str_min="0",str_sec="0";
					
					int pos1=str_dur.indexOf("\u9577\u3055:");  //  "長さ:"
					int pos2=str_dur.indexOf("\u6642\u9593");	//  "時間"   
					int pos3=str_dur.indexOf("\u5206");			//  "分"   
					int pos4=str_dur.indexOf("\u79D2\u3002");	//  "秒。" 
					
					try {
						if(pos1>0 && prev_obj!=null && (prev_obj instanceof WebVideoStream)) {
							if(pos2>0) {
								str_hour=str_dur.substring(pos1+3,pos2);
								if(pos3>0) {
									str_min=str_dur.substring(pos2+3,pos3);
									if(pos4>0) {
										str_sec=str_dur.substring(pos3+2,pos4);
									}
								}
							}
							else if(pos3>0) {
								str_min=str_dur.substring(pos1+3,pos3);
								if(pos4>0) {
									str_sec=str_dur.substring(pos3+2,pos4);
								}
							}
							else if(pos4>0) {
								str_sec=str_dur.substring(pos1+3,pos4);
							}
							tempDuration=Float.parseFloat(str_hour)*3600+Float.parseFloat(str_min)*60+Float.parseFloat(str_sec);
							if(PMS.rz_debug>1) {
								PMS.dbg("Duration found: text="+str_dur+", sample="+" - \u9577\u3055: 4 \u5206\u300153 \u79D2\u3002");
								PMS.dbg("Duration found: pos1="+pos1+", pos2="+pos2+", pos3="+pos3);
								PMS.dbg("Duration found: text="+str_dur+", --> extructed hour="
									+str_hour+", min="+str_min+", sec="+str_sec);
								PMS.dbg("Duration Final calced sec="+tempDuration);
							}
							VideosFeed.manage_setDur((WebVideoStream)prev_obj,tempDuration);
						}
					} catch(IndexOutOfBoundsException e) {
						PMS.dbg("Duration parse error="+e.getMessage());
					} catch(NumberFormatException e) {
						PMS.dbg("Duration parse error="+e.getMessage());
					}
				}
				else if(vid.tagName().equals("img")) {
					thumbdt=vid.attr("data-thumb");
					setTempItemThumbURL(thumbdt);
					if(PMS.rz_debug>1) PMS.dbg("found thumb url="+thumbdt);
				}
				else if(vid.tagName().equals("a")) { 
					vidurl=vid.attr("href");
					if(url.contains("/www.youtube.com/") && !vidurl.startsWith("http")) {  //relative path
						vidurl="https://www.youtube.com"+vidurl;  //to be refined for not youtube
					}
					vidname=vid.text();
					if(PMS.rz_debug>1) {
						PMS.dbg("parse_html_google_video: found valid tag link="+vidurl);
						PMS.dbg("parse_html_google_video: found valid tag name="+vidname);
					}
					//if(prevurl==null) {//1st entry <a href= ...>
					if(vidurl!=null && !vidurl.equals(prevurl)) {//1st entry <a href= ...>
						if(vidurl.contains("//webcache.google")) {
							//---- web cache ---> ignore
							continue;
						}
						//PMS.dbg("parse_html_google_video: 1st entry <a href>");
						setTempItemTitle(vidname);
						setTempItemLink(vidurl);
						setTempFeedLink(null);
						//setTempItemThumbURL(null);
						do_sort= -1;
						prevurl=vidurl;
						boolean feed=false;
						if(url.contains("www.youtube.com/user/") || url.contains("www.youtube.com/channel/")) {
							//https://www.youtube.com/user/exploreTeam
							if(url.endsWith("/videos")) {
								//---- User's Upload list page
								if(vidurl.contains("www.youtube.com/watch?v")) {
									// go-on as videostream
								}
								else vidurl=null;
							}
							else if(url.contains("/playlists")) {
								//---- User's Playlists page
								if(vidurl.contains("www.youtube.com/playlist?list=")) {
									//PMS.dbg("found playlist: name="+vidname+", url="+vidurl);
									setTempFeedLink(vidurl);
									//do_sort=PMS.SORT_NAME_NUM;
								}
								else vidurl=null;
							}
							else if(url.endsWith("/channels")) {
								//---- User's Favorite Channels page
								//ex. url   =https://www.youtube.com/channel/UC-9-kyTW8ZkZNDHQJ6FgpwQ/channels
								//ex. vidurl=https://www.youtube.com/channel/UCE80FOXpJydkkMo-BYoJdEg
								if(vidurl.contains("www.youtube.com/channel/")) {
									if(vidurl.endsWith("/videos") || vidurl.endsWith("/playlists"))  {
										vidurl=null;
									}
									else {
										//PMS.dbg("Node found: name="+vidname+", url="+vidurl);
										setTempFeedLink(vidurl);
									}
								}
								else vidurl=null;
							}
							else {  
								//---- User or Channel it's self (Home page)
								if(vidurl.endsWith("videos")) {
									//PMS.dbg("Node VIDEOS found: name="+vidname+", url="+vidurl);
									if(url.contains("www.youtube.com/user/") && plistid_upld!=null) {
									//if(plistid_upld!=null) {
										vidurl="https://www.youtube.com/playlist?list="+plistid_upld;
										if(PMS.rz_debug>1) PMS.dbg("Substitute User upload_videos to playlist url="+vidurl);
									}
									setTempFeedLink(vidurl);
								}
								else if(vidurl.endsWith("playlists")) {
									//vidurl=vidurl+"?shelf_id=0&view=1&sort=dd";
									vidurl=vidurl+"?view=1&sort=lad";
									//PMS.dbg("Node PLAYLISTS found: name="+vidname+", url="+vidurl);
									setTempFeedLink(vidurl);
									do_sort=PMS.SORT_NAME_NUM;
								}
								else if(vidurl.endsWith("channels")) {
									//PMS.dbg("Node CHANNELS found: name="+vidname+", url="+vidurl);
									setTempFeedLink(vidurl);
								}
								else if(vidurl.contains("www.youtube.com/watch?v")) {
									// go-on as videostream
								}
								else if(vidurl.contains("www.youtube.com/channel/")) {
									//avoid multi-creation
									if(vidurl.equals(url) || (channel_id!=null &&  vidurl.contains("/channel/"+channel_id))) {  // it's me
										//PMS.dbg("Entry="+vidname+": is myself, no need in this Feed");
										vidurl=null;
									}
									else if(rFolder!=null) {
										for(DLNAResource cs : rFolder.getChildren()) {
											//PMS.dbg("VideosFeed.manageItem: Existing url2="+url2);
											if(vidurl.equals(cs.getSrcPath())) {
												//PMS.dbg("Same Entry="+vidname+": already exists under this Feed");
												vidurl=null;
												break;
											}
										}
									}
									if(vidurl!=null) {
										//create folder
										if(rFolder==null) {
											rFolder= new VirtualFolder("Related Links", null);
											addChild(rFolder);
											rFolder.sort_enabled=true;
											rFolder.rz_MetaSortType=PMS.SORT_NAME_NUM;
										}
										//create entry
										//PMS.dbg("Channel("+vidname+") found: put under [Related Links], thumb="+getTempItemThumbURL());
										setTempFeedLink(vidurl);
										//setTempItemThumbURL(vidurl);
										DLNAResource ch=manageItem(0);
										rFolder.addChild(ch);
										vidurl=null;  //done
									}
								}
								else vidurl=null;
							}
						}
						else {  //google search results or youtube-playlists 
							if(vidurl.contains("www.youtube.com/watch?v")) {
								//PMS.dbg("found video: name="+vidname+", url="+vidurl);
							}
							else if(vidurl.contains("www.youtube.com/playlist?list=")) {
								// you are in user's playlist page
								//PMS.dbg("found playlist: name="+vidname+", url="+vidurl);
								setTempFeedLink(vidurl);
							}
						}
					}
					else {
						//PMS.dbg("2nd entry : vidurl="+vidurl+", prevurl="+prevurl+", prev_obj="+prev_obj
						//	+" , instanceof="+(prev_obj instanceof WebVideoStream)); 
						
						if(vidurl.equals(prevurl) && prev_obj!=null && prev_obj instanceof WebVideoStream) {	
							//2nd entry of <a href= ...> may contain duration/thumbnailid
							//PMS.dbg("parse_html_google_video: 2nd entry <a href>");
							if(vidname!=null && vidname.length()>2) {
								tempDuration=getDuration(vidname.substring(2).trim());
							}
							else {
								//PMS.dbg("No.="+cnt+", Illegal duration_time string="+vidname);
								tempDuration=36000-1;
							}
							if(true) { //get thumbnail
								Elements imgs=vid.select(ptn_thumbid);
								Iterator<org.jsoup.nodes.Element> ei2=imgs.iterator();
								vidimg=null;
								while(ei2.hasNext()) {
									org.jsoup.nodes.Element img=ei2.next();
									vidimg=img.attr("id");
									if(vidimg!=null) { 
										thumbdt=thumbtb.get(vidimg);
										//thumbdt=img.attr("src");
										if(PMS.rz_debug>1) PMS.dbg("parse_html_google_video: thumb-id="+vidimg+", thumb data="+thumbdt);
										break;
									}
								}
							}
							VideosFeed.manage_setDur((WebVideoStream)prev_obj,tempDuration);
							VideosFeed.manage_setThumb((WebVideoStream)prev_obj,thumbdt);
						}
						vidurl=null;  //no create
					}
				}
				if(vidurl!=null) {
					i++;
					cnt++;
					if(PMS.rz_debug>1) {
						PMS.dbg("parse_html_google_video: ==== Creating entry No."+cnt+", name="+getTempItemTitle());
						PMS.dbg("parse_html_google_video: URL="+getTempItemLink());
						PMS.dbg("parse_html_google_video: tempDuration="+tempDuration);
						PMS.dbg("parse_html_google_video: Thumbnail url(id)="+getTempItemThumbURL());
					}
					setTempSerialId(i); //regzamod
					prev_obj=manageItem();
					if(prev_obj!=null && do_sort>=0) {
						prev_obj.sort_enabled=true;
						//prev_obj.doSort=true;
						prev_obj.rz_MetaSortType=do_sort;
					}
					setTempItemThumbURL(null);
				}
				else {
					prev_obj=null;
				}
			}
		}
		
		if(!forced_name) {
			//forced_name=false;
			if(title==null) 
				setName("Search Results");
			else 
				setName(title.text());
		}
		setLastmodified(System.currentTimeMillis());
		return cnt;
	}
	
	public int parse_html_ytpls(String url) throws Exception {
		//youtube playlist for browser, not api
		Document doc=null;
		String agent="Firefox/26.0";

		if(false) {
			byte b[] = downloadAndSendBinary(url);
			String content = new String(b, "UTF-8");
		}
		
		try {
		    if(url.startsWith("file:")) {
		    	url=url.substring("file:".length());
			    File f= new File(url);
			    doc = DataUtil.load(f,"UTF-8",url);
		    }
		    else {
		    	doc = Jsoup.connect(url).header("User-Agent",agent).get();
		    }
		} catch (IOException e) {
			if(PMS.rz_debug>1) {
				logger.error("Feed.parse_html_ytpls: Error in getFeed url=" + url, e);
			}
			else {
				logger.warn("Feed.parse_html_ytpls: Error in getFeed url==" + url+", \nErrorMsg="+e.getMessage());
			}
		    //e.printStackTrace();
		}		
		if (doc == null) {
			return -1;
		}
		Elements title=doc.getElementsByTag("title");
		Elements vids=doc.select("tr[class*=pl-video]"); 

		//ToDo: select by more refined way
		/* Sample
		<tr class="pl-video yt-uix-tile " data-set-video-id="" data-title="鶏肉の溶岩焼き　（陣や@溝の口）" 
			data-video-id="h6lgAwk1XeM">
			<td class="pl-video-handle "></td>
			<td class="pl-video-index"></td>
			<td class="pl-video-thumbnail">
				<span class="pl-video-thumb ux-thumb-wrap contains-addto">
				<a href="/watch?v=h6lgAwk1XeM&amp;list=PLmjuevQrM8DA1EpfZ9Cu5hAFE7WKZtOhn&amp;index=1" class="yt-uix-sessionlink" aria-hidden="true"  data-sessionlink="feature=plpp_video&amp;ei=o2xxVbOEH5-AoQP7hYCoBA&amp;ved=CA0QxjQ" >  
					<span class="video-thumb  yt-thumb yt-thumb-72">
						<span class="yt-thumb-default">
						<span class="yt-thumb-clip">
							<img src="https://s.ytimg.com/yts/img/pixel-vfl3z5WfW.gif" 
								data-thumb="//i.ytimg.com/vi/h6lgAwk1XeM/default.jpg" alt="" width="72" aria-hidden="true" >
						<span class="vertical-align"></span>
						</span>
					    </span>
					</span>
				</a>  
				<span class="thumb-menu dark-overflow-action-menu video-actions">
					<button type="button" class="yt-uix-button-reverse flip addto-watch-queue-menu spf-nolink hide-until-delayloaded yt-uix-button yt-uix-button-dark-overflow-action-menu yt-uix-button-size-default yt-uix-button-has-icon no-icon-markup yt-uix-button-empty" onclick=";return false;" aria-haspopup="true" aria-expanded="false" ><span class="yt-uix-button-arrow yt-sprite"></span><ul class="watch-queue-thumb-menu yt-uix-button-menu yt-uix-button-menu-dark-overflow-action-menu hid"><li role="menuitem" class="overflow-menu-choice addto-watch-queue-menu-choice addto-watch-queue-play-next yt-uix-button-menu-item" data-action="play-next" onclick=";return false;"  data-video-ids="h6lgAwk1XeM"><span class="addto-watch-queue-menu-text">次に再生</span></li><li role="menuitem" class="overflow-menu-choice addto-watch-queue-menu-choice addto-watch-queue-play-now yt-uix-button-menu-item" data-action="play-now" onclick=";return false;"  data-video-ids="h6lgAwk1XeM"><span class="addto-watch-queue-menu-text">今すぐ再生</span></li></ul></button>
				</span>
				<button class="yt-uix-button yt-uix-button-size-small yt-uix-button-default yt-uix-button-empty yt-uix-button-has-icon no-icon-markup addto-button video-actions spf-nolink hide-until-delayloaded addto-watch-later-button yt-uix-tooltip" type="button" onclick=";return false;" role="button" title="後で見る" data-video-ids="h6lgAwk1XeM"></button>
				<button class="yt-uix-button yt-uix-button-size-small yt-uix-button-default yt-uix-button-empty yt-uix-button-has-icon no-icon-markup addto-button addto-queue-button video-actions spf-nolink hide-until-delayloaded addto-tv-queue-button yt-uix-tooltip" type="button" onclick=";return false;" title="TV キュー" data-video-ids="h6lgAwk1XeM" data-style="tv-queue"></button>
				</span>
			</td>  
			<td class="pl-video-title">
			    <a class="pl-video-title-link yt-uix-tile-link yt-uix-sessionlink  spf-link " dir="ltr" href="/watch?v=h6lgAwk1XeM&amp;list=PLmjuevQrM8DA1EpfZ9Cu5hAFE7WKZtOhn&amp;index=1" data-sessionlink="feature=plpp_video&amp;ei=o2xxVbOEH5-AoQP7hYCoBA&amp;ved=CA0QxjQ">
			      鶏肉の溶岩焼き　（陣や@溝の口）
			    </a>
			    <div class="pl-video-owner">作成者: 
					<a href="/user/cocoism3" class=" yt-uix-sessionlink     spf-link  g-hovercard" data-ytid="UCWuGWqlTp0_To1eJuSBvLMg" data-sessionlink="feature=playlist&amp;ei=o2xxVbOEH5-AoQP7hYCoBA&amp;ved=CA0QxjQ" data-name="playlist">Yuko Maeda</a>
				</div>
			</td>
			<td class="pl-video-badges"></td>
			<td class="pl-video-added-by"></td>
			<td class="pl-video-time">
			<div class="more-menu-wrapper">  
			<div class="timestamp">
				<span title="14 秒">0:14</span>  		//old pattern
				<span aria-label="62 秒">1:02</span>	//new pattern
			</div>
			<div class="pl-video-edit-options"></div></td>
		</tr>
		*/
		
		Iterator<org.jsoup.nodes.Element> ei=vids.iterator();
		Iterator<org.jsoup.nodes.Element> ei2;
		Elements elms,elms1;
		String str,vidurl=null,vidname,vidimg,prevurl=null;

		long i=10;
		int cnt=0;
		while(ei.hasNext()) {
			vidurl=null;
			vidname=null;
			vidimg=null;
			org.jsoup.nodes.Element vid=ei.next();
			//PMS.dbg("parse_html_ytpls: tag found="+vid);
			
			/*
			<tr class="pl-video yt-uix-tile " data-set-video-id="" data-title="鶏肉の溶岩焼き　（陣や@溝の口）" 
			data-video-id="h6lgAwk1XeM">
			*/
			
			//---- video url 
			vidurl=vid.attr("data-video-id");
			vidurl="http://www.youtube.com/watch?v="+vidurl;
			//PMS.dbg("parse_html_ytpls: tag href="+vidurl);
			
			//---- video title 
			vidname=vid.attr("data-title");
			//PMS.dbg("parse_html_ytpls: tag text="+vidname);
			
			//---- video img(thumbnail url)
			elms=vid.select("img[data-thumb]");  //tag[attribute]
			ei2=elms.iterator();
			while(ei2.hasNext()) {
				org.jsoup.nodes.Element el=ei2.next();
				vidimg=el.attr("data-thumb");
				if(vidimg.startsWith("//")) {
					vidimg="http:"+vidimg;
				}
				//PMS.dbg("parse_html_ytpls: img_thumb_url found="+vidimg);
				break;
			}
			//---- video duration
			/*
			<div class="timestamp">
				<span title="14 秒">0:14</span>  		//old pattern
				<span aria-label="62 秒">1:02</span>	//new pattern
			</div>
			*/
			int dur=0;
			if(false) {  //good only for old
				elms=vid.select("span[title]");  //el[attr(=sss)]: elements with attribute, e.g. a[href]
				ei2=elms.iterator();
				while(ei2.hasNext()) {
					org.jsoup.nodes.Element el=ei2.next();
					dur=getDuration(el.text().trim());
					break;
				}
			}
			else { //good for both new/old
				elms=vid.select("div.timestamp");  //tag.class
				elms1=elms.select("span");
				ei2=elms1.iterator();
				while(ei2.hasNext()) {
					org.jsoup.nodes.Element el=ei2.next();
					dur=getDuration(el.text().trim());
					break;
				}
			}
			//PMS.dbg("parse_html_ytpls: duration ="+dur);
			
			i++;
			cnt++;
			tempDuration=dur;
			setTempItemTitle(vidname);
			setTempItemLink(vidurl);
			setTempFeedLink(null);
			setTempItemThumbURL(vidimg);
			setTempSerialId(i); //regzamod
			manageItem();
		}
		//PMS.dbg("feed: presetted name="+getName());
		if(!forced_name) {
			//forced_name=false;
			if(title==null) 
				setName("Search Results");
			else 
				setName(title.text());
		}
		setLastmodified(System.currentTimeMillis());
		return cnt;
	}

	@SuppressWarnings("unchecked")
	private void parseElement(Element elt, boolean parseLink) {
		//PMS.dbg("parseElement: elt.name="+elt.getName());
		if ("content".equals(elt.getName()) && "media".equals(elt.getNamespacePrefix())) {
			if (parseLink) {
				setTempItemLink(elt.getAttribute("url").getValue());
				// not come here
				//String duration=elt.getAttribute("duration").getValue();	//regzamod,add
				//PMS.dbg("Feed.parseElement: duration1="+duration);	//regzamod,add
			}
			if(tempDuration<=0 && elt.getAttribute("duration")!=null) {
				tempDuration=Float.parseFloat(elt.getAttribute("duration").getValue());
				tempDuration+=duration_add;  //tuning for avoid non-zero start time on REGZA
				
				//PMS.dbg("Feed.parseElement: duration="+tempDuration);	//regzamod,add
			}
				
			List<Content> subElts = elt.getContent();
			for (Content subelt : subElts) {
				if (subelt instanceof Element) {
					parseElement((Element) subelt, false);
				}
			}
		}
		
		if ("thumbnail".equals(elt.getName()) && "media".equals(elt.getNamespacePrefix())) {
			//PMS.dbg("Feed.parseElement: thumbnail called");
			if (getTempItemThumbURL() == null) {
				setTempItemThumbURL(elt.getAttribute("url").getValue());
			}
		}
		if ("image".equals(elt.getName()) && "exInfo".equals(elt.getNamespacePrefix())) {
			if (getTempItemThumbURL() == null) {
				setTempItemThumbURL(elt.getValue());
			}
		}
	}
	
	private int getDuration(String timstr) {
		String tim[]=timstr.split(":");
		int dur=0;
		try {
			if(tim.length==3) {
				int hr =Integer.parseInt(tim[0]);
				int min=Integer.parseInt(tim[1]);
				int sec=Integer.parseInt(tim[2]);
				dur=hr*3600+min*60+sec;
			}
			else if(tim.length==2) {
				int min=Integer.parseInt(tim[0]);
				int sec=Integer.parseInt(tim[1]);
				dur=min*60+sec;
			}
		} catch (NumberFormatException e) {
		}
		//PMS.dbg("getDuration: duration string="+timstr+", time(sec)="+dur);
		return dur;
	}

	public InputStream getInputStream() throws IOException {
		return null;
	}

	/*
	public String getName() {
		return name;
	}
	@Override
	public String getDpName() {	//regzamod
		return getName();
	}
	*/

	public boolean isFolder() {
		return true;
	}

	public long length() {
		return 0;
	}

	public long lastModified() {
		return 0;
	}

	@Override
	public String getSystemName() {
		return url;
	}

	@Override
	public boolean isValid() {
		return true;
	}

	protected DLNAResource manageItem () {
		return manageItem(1);
	}

	protected DLNAResource manageItem (int mode) { 
		//PMS.dbg("Feed.manageItem: called");
		int type=getSpecificType();
		String thumbURL=getTempItemThumbURL();
		
		if(PMS.rz_debug>1) {
			PMS.dbg("Feed.manageItem: feed="+getTempFeedLink()+", url="+getTempItemLink());
			PMS.dbg("Feed.manageItem: thumbURL="+thumbURL);
		}
		//---- item is playlist?
		//video item doesn't come here!! see VideosFeed.java
		//if(getTempItemLink()!=null 
			//  && (getTempItemLink().contains("www.youtube.com/playlist?list=")
			//   || getTempItemLink().contains("www.youtube.com/user/"))
			//){
		if(getTempFeedLink()!=null && type==Format.VIDEO) { 
			//PMS.dbg("Feed.manageItem: found Feed(playlist/uploads) --> Add Feed subfolder");
			//VideosFeed ch=new VideosFeed(getTempItemLink(),getTempItemTitle());
			VideosFeed ch=new VideosFeed(getTempFeedLink(),getTempItemTitle());
			if(mode==1) addChild(ch);
			return (DLNAResource)ch;
		}
		
		//---- item is normal feed item
		FeedItem fi = new FeedItem(getTempItemTitle(), getTempItemLink(), thumbURL, null, type);
		fi.setSerialId(getTempSerialId());//regzamod
		fi.wmed_duration=tempDuration;//regzamod
		if(PMS.rz_debug>1) PMS.dbg("Feed.manageItem: duration="+tempDuration);	//regzamod,add
		if(mode==1) addChild(fi);
		
		//---- thumbnail
		if(type==Format.IMAGE || type==Format.VIDEO) {
			//type==Format.VIDEO never comes here: b/c manageItem() is overrided by VideosFeed.java
			//PMS.dbg("Feed.manageItem: type="+type);
			if(thumbURL!=null && thumbURL.startsWith("data:image/")) {
				//---- mediainfo
				//PMS.dbg("Feed.manageItem: thumbURL!=null && thumbURL.startsWith(\"data:image/\")");
				DLNAMediaInfo med;
				if(fi.getMedia()==null) {
					fi.setMedia(new DLNAMediaInfo());
				}
				med=fi.getMedia();
				//wmed_valid=true;
				
				//inline image data : decode by base64.decoder
				int pos=thumbURL.indexOf(";base64,");
				if(pos>0) {
					int len=thumbURL.length();
					//PMS.dbg("Feed.manageItem: thumbURL seems to be inline image, len="+len+", data top="+thumbURL.substring(0,60));
					//PMS.dbg("Feed.manageItem: data body="+thumbURL.substring(pos+8,len));
					med.setThumb(MediaInfoParser.getCover(thumbURL.substring(pos+8,len)));
					med.setThumbready(true);
					med.setMediaparsed(true); //fake, some values setted
					fi.setThumbURL(null);
				}
			}
		}
		return (DLNAResource)fi;
	}
		
	
	//private Long refreshIntervalWeb=(long)3600000;	//original, msec (60 minutes)
	public static Long refreshIntervalWeb=(long)600000;		//regzamod, msec (10 minutes)
	@Override
	/* origin
	public boolean isRefreshNeeded() {
	    return (System.currentTimeMillis() - getLastmodified() > 3600000);
	}
	*/
	//---- regzamod add start
	public boolean isRefreshNeeded() { //for folder
	    Long t=System.currentTimeMillis() - getLastmodified();
		//PMS.dbg("Feed.isRefreshNeeded : Remainig time for nextRefresh="+(refreshIntervalWeb - t)
		//	+" msecs, (refresh Interval="+refreshIntervalWeb+" msec)");
		
	    if(t>refreshIntervalWeb || getLastmodified() < PMS.dt_web_update_time) { 
			//PMSUtil.strace();
			//PMS.dbg("Feed.isRefreshNeeded : over refresh interval : return true");
	    	resolved=false;
	    	return true;
	    }
		else if(!resolved) {
			//PMS.dbg("Feed.isRefreshNeeded : not resolved : return true");
	    	return true;
		}
		else {
			//PMS.dbg("Feed.isRefreshNeeded : return false");
	    	return false;
	    }
	}
	
	protected String[] url_params;	//regzamod
	
	public void setUrlParams(String[] up, String cs) {
		
		if(PMS.rz_debug>1) {
			PMS.dbg("==== Feed.setUrlParams: setUrlParams up="+up+", cs="+cs);
			if(up!=null) {
				for(int i=1;i<up.length;i++) {
					PMS.dbg("Feed.setUrlParams: param up("+i+") ="+up[i]);
				}
			}
			PMS.dbg("Feed.setUrlParams: param cs="+cs);
		}
		
		String[] cp=null;
		if(cs!=null) {
			cp=cs.split(",");
		}
		
		//up: valid from [1] for opts, i.e. [0]:url, [1]:opts,...
		url_params=StringUtil.StrArrayConcat(cp,up,0,1);
		
		if(PMS.rz_debug>1) {
			if(url_params!=null && url_params.length>0) {
				for(int i=0;i<url_params.length;i++) {
					PMS.dbg("Feed.setUrlParams: param("+i+") ="+url_params[i]);
				}
			}
		}
		
		String[] p=url_params;
		if(p==null) {
			return;
		}

		boolean sort_sv=sort_enabled;
		String str;
		for(int i=0;i<p.length;i++) {
			if(p[i]!=null) {
				str=null;
				if(p[i].startsWith("sort=")) {
					str=p[i].substring(5).trim();
				}
				else if(p[i].startsWith("sort_type=")) {
					str=p[i].substring(10).trim();
				}
				if(str!=null) {
					//PMS.dbg("Feed.setUrlParams: sort option found --> set doSort=true");
					sort_enabled=true;
					doSort=true;
					//PMS.dbg("Feed: param("+i+") ="+p[i]);
					if(str.equals("name")) 			rz_MetaSortType=PMS.SORT_NAME;
					else if(str.equals("name_num"))	rz_MetaSortType=PMS.SORT_NAME_NUM;
					else if(str.equals("name_asc"))	rz_MetaSortType=PMS.SORT_NAME_ASC;
					else if(str.equals("date"))		rz_MetaSortType=PMS.SORT_DATE;
					else if(str.equals("date_r"))	rz_MetaSortType=PMS.SORT_DATE_R;
					else if(str.equals("default"))	rz_MetaSortType=PMS.SORT_DEFAULT;
					else if(str.equals("def"))		rz_MetaSortType=PMS.SORT_DEFAULT;
					else if(str.equals("original"))	rz_MetaSortType=PMS.SORT_ORIGIN;
					else if(str.equals("origin"))	rz_MetaSortType=PMS.SORT_ORIGIN;
					else if(str.equals("org"))		rz_MetaSortType=PMS.SORT_ORIGIN;
					else {
						sort_enabled=sort_sv;
						doSort=false;
						logger.error("Feed: name="+dname+", Unknown sort_type="+str);
					}
				}
			}
		}
	}
	
	@Override
	public String[] getUrlParams() {
		return url_params;
	}
	
	public void setUrlParamsString(String s) {
		String s1="dummy_url,"+s;
		url_params=s1.split(",");
	}
	
	@Override
	public String getUrlParamsString() {
		if(url_params==null) return null;
		String s="";
		for(int i=0;i<url_params.length;i++) {
			s+=","+url_params[i];
		}
		if(s.length()>0) {
			s=s.substring(1,s.length());
		}
		else {
			s=null;
		}
		return s;
	}

	//regzamod add
	public boolean needSort() {
		return sort_enabled;
	}

	//---- regzamod add end

	@Override
	public void refreshChildren() {
		if(PMS.rz_debug>1) PMS.dbg("Feed.refreshChildren : called");	//regzamod
		resolved=false;
		resolve_in();
		
		/*
		try {
			resolved=false;
			resolve_in();
		} catch (Exception e) {
			if(PMS.rz_debug>1) {
				logger.error("Error in parsing stream: " + url, e);
			}
			else {
				PMS.dbg("Feed.parse_feed: Error in parsing stream=" + url+", \nError="+e.getMessage());
			}
		}
		*/
	}

	/**
	 * This method ensures that the output String has only
	 * valid XML unicode characters as specified by the
	 * XML 1.0 standard. For reference, please see
	 * <a href="http://www.w3.org/TR/2000/REC-xml-20001006#NT-Char">the
	 * standard</a>. This method will return an empty
	 * String if the input is null or empty.
	 *
	 * @param in The String whose non-valid characters we want to remove.
	 * @return The in String, stripped of non-valid characters.
	 */
	private String stripNonValidXMLCharacters2(String in) {		// regzamod for test --> No effects
		return in;
	}
	private String stripNonValidXMLCharacters(String in) {		// Origin
		StringBuilder out = new StringBuilder(); // Used to hold the output.
		char current; // Used to reference the current character.

		if (in == null || ("".equals(in))) {
			return ""; // vacancy test.
		}
		for (int i = 0; i < in.length(); i++) {
			current = in.charAt(i); // NOTE: No IndexOutOfBoundsException caught here; it should not happen.
			if ((current == 0x9)
				|| (current == 0xA)
				|| (current == 0xD)
				|| ((current >= 0x20) && (current <= 0xD7FF))
				|| ((current >= 0xE000) && (current <= 0xFFFD))
				|| ((current >= 0x10000) && (current <= 0x10FFFF))) {
				out.append(current);
			}
		}
		return out.toString();
	}

	/**
	 * @return the url
	 * @since 1.50
	 */
	protected String getUrl() {
		return url;
	}
		
	public String getSrcPath(){
		return url;
	}
	
	/**
	 * @param url the url to set
	 * @since 1.50
	 */
	protected void setUrl(String url) {
		this.url = url;
	}

	protected long getTempSerialId() {	//regzamod, add
		return tempSerialId;
	}
	protected void setTempSerialId(long tempSerialId) {	//regzamod, add
		this.tempSerialId=tempSerialId;
	}
	
	/**
	 * @return the tempItemTitle
	 * @since 1.50
	 */
	protected String getTempItemTitle() {
		return this.tempItemTitle;
	}

	/**
	 * @param tempItemTitle the tempItemTitle to set
	 * @since 1.50
	 */
	protected void setTempItemTitle(String tempItemTitle) {
		this.tempItemTitle = tempItemTitle;
	}

	/**
	 * @return the tempItemLink
	 * @since 1.50
	 */
	protected String getTempItemLink() {
		return this.tempItemLink;
	}

	/**
	 * @param tempItemLink the tempItemLink to set
	 * @since 1.50
	 */
	protected void setTempItemLink(String tempItemLink) {
		this.tempItemLink = tempItemLink;
	}

	/**
	 * @return the tempFeedLink
	 * @since 1.50
	 */
	protected String getTempFeedLink() {
		return this.tempFeedLink;
	}

	/**
	 * @param tempFeedLink the tempFeedLink to set
	 * @since 1.50
	 */
	protected void setTempFeedLink(String tempFeedLink) {
		this.tempFeedLink = tempFeedLink;
	}

	/**
	 * @return the tempCategory
	 * @since 1.50
	 */
	protected String getTempCategory() {
		return tempCategory;
	}

	/**
	 * @param tempCategory the tempCategory to set
	 * @since 1.50
	 */
	protected void setTempCategory(String tempCategory) {
		this.tempCategory = tempCategory;
	}

	/**
	 * @return the tempItemThumbURL
	 * @since 1.50
	 */
	protected String getTempItemThumbURL() {
		return tempItemThumbURL;
	}

	/**
	 * @param tempItemThumbURL the tempItemThumbURL to set
	 * @since 1.50
	 */
	protected void setTempItemThumbURL(String tempItemThumbURL) {
		this.tempItemThumbURL = tempItemThumbURL;
	}

	/**
	 * @param name the name to set
	 * @since 1.50
	 */
	/*
	@Override
	protected void setName(String name) {
		this.name = name;
	}
	*/
	
}
