diff --git a/API_REFERENCE b/API_REFERENCE index d84590eb..79431747 100644 --- a/API_REFERENCE +++ b/API_REFERENCE @@ -65,3 +65,7 @@ getAlbumInfo&id=$albumid (See above, returns Summary and Content) getArtistThumb&id=$artistid (Returns either a relative path to the cached thumbnail artist image, or an http:// address if the cache dir can't be written to) getAlbumThumb&id=$albumid (see above) + +choose_specific_download&id=$albumid (Gives you a list of results from searcher.searchforalbum(). Basically runs a normal search, but rather than sorting them and downloading the best result, it dumps the data, which you can then pass on to download_specific_release(). Returns a list of dictionaries with params: title, size, url, provider & kind - all of these values must be passed back to download_specific_release) + +download_specific_release&id=albumid&title=$title&size=$size&url=$url&provider=$provider&kind=$kind (Allows you to manually pass a choose_specific_download release back to searcher.send_to_downloader()) \ No newline at end of file diff --git a/Headphones.py b/Headphones.py index 19cc82dd..480caf08 100755 --- a/Headphones.py +++ b/Headphones.py @@ -14,7 +14,12 @@ # You should have received a copy of the GNU General Public License # along with Headphones. If not, see . -import os, sys, locale +import os, sys + +# Ensure lib added to path, before any other imports +sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'lib/')) + +import locale import time import signal @@ -34,7 +39,6 @@ signal.signal(signal.SIGTERM, headphones.sig_handler) def main(): - # Fixed paths to Headphones if hasattr(sys, 'frozen'): headphones.FULL_PATH = os.path.abspath(sys.executable) @@ -142,7 +146,7 @@ def main(): # Force the http port if neccessary if args.port: http_port = args.port - logger.info('Using forced port: %i' % http_port) + logger.info('Using forced port: %i', http_port) else: http_port = int(headphones.HTTP_PORT) @@ -172,7 +176,7 @@ def main(): except KeyboardInterrupt: headphones.SIGNAL = 'shutdown' else: - logger.info('Received signal: ' + headphones.SIGNAL) + logger.info('Received signal: %s', headphones.SIGNAL) if headphones.SIGNAL == 'shutdown': headphones.shutdown() elif headphones.SIGNAL == 'restart': diff --git a/TODO b/TODO deleted file mode 100644 index f9066c0e..00000000 --- a/TODO +++ /dev/null @@ -1,8 +0,0 @@ -############################## -### TODO LIST (18/08/12) ### -############################## - -1. Add ability to fetch a specific extra, rather than pull down the whole category - - This requires extra calls to MB right now, so modify 1st mb artist call to pull down the - whole album list, then only send the desried ones to importer.py -2. Update API with newstyle getExtras function (lacking entirely right now) diff --git a/data/interfaces/default/album.html b/data/interfaces/default/album.html index 008dac4e..98dc51ba 100644 --- a/data/interfaces/default/album.html +++ b/data/interfaces/default/album.html @@ -5,8 +5,13 @@ %> <%def name="headerIncludes()"> +
+
+ Delete Album %if album['Status'] == 'Skipped': Mark Album as Wanted @@ -59,9 +64,23 @@
+ Choose Specific Download +
- « Back to ${album['ArtistName']} <%def name="body()"> @@ -89,7 +108,6 @@
  • Duration: ${albumduration}
  • -
    @@ -197,9 +215,66 @@ "bInfo": false, "bPaginate": false, "bDestroy": true - }); + }); }; - + + function getAvailableDownloads() { + ShowSpinner(); + $.getJSON("choose_specific_download?AlbumID=${album['AlbumID']}", function(data) { + loader.remove(); + feedback.fadeOut(); + search_results = data + for( var i = 0, len = data.length; i < len; i++ ) { + $('#downloads_table_body').append(''); + } + $('#downloads_table').dataTable({ + "aoColumns": [ + null, + { "sType": "title-numeric"}, + null, + null + ], + "aaSorting": [], + "bFilter": false, + "bInfo": false, + "bPaginate": false, + }); + $("#choose_specific_download_dialog").dialog({ width: "1000px" }); + return false; + }); + } + + function downloadSpecificRelease(i){ + + title = search_results[i].title + size = search_results[i].size + url = search_results[i].url + provider = search_results[i].provider + kind = search_results[i].kind + + ShowSpinner(); + $.getJSON("download_specific_release?AlbumID=${album['AlbumID']}&title="+title+"&size="+size+"&url="+url+"&provider="+provider+"&kind=" + kind, function(data) { + loader.remove(); + feedback.fadeOut(); + refreshSubmenu(); + $("#choose_specific_download_dialog").dialog("close"); + }); + } + + function ShowSpinner() { + feedback = $("#ajaxMsg"); + update = $("#updatebar"); + if ( update.is(":visible") ) { + var height = update.height() + 35; + feedback.css("bottom",height + "px"); + } else { + feedback.removeAttr("style"); + } + loader = $(""); + feedback.prepend(loader); + feedback.fadeIn(); + } + $(document).ready(function() { getAlbumInfo(); initThisPage(); diff --git a/data/interfaces/default/artist.html b/data/interfaces/default/artist.html index 33c27a6c..06bb7ba6 100644 --- a/data/interfaces/default/artist.html +++ b/data/interfaces/default/artist.html @@ -193,7 +193,7 @@ template = '
  • NAMELOC
  • '; - $.getJSON("http://api.songkick.com/api/3.0/artists/mbid:${artist['ArtistID']}/calendar.json?apikey=${headphones.SONGKICK_APIKEY}&jsoncallback=?", + $.getJSON("https://api.songkick.com/api/3.0/artists/mbid:${artist['ArtistID']}/calendar.json?apikey=${headphones.SONGKICK_APIKEY}&jsoncallback=?", function(data){ if (data['resultsPage'].totalEntries >= 1) { @@ -265,6 +265,7 @@ "aoColumnDefs": [ { 'bSortable': false, 'aTargets': [ 0,1 ] } ], + "bStateSave": true, "oLanguage": { "sLengthMenu":"Show _MENU_ albums per page", "sEmptyTable": "No album information available", diff --git a/data/interfaces/default/base.html b/data/interfaces/default/base.html index e7d26f1a..326a02cf 100644 --- a/data/interfaces/default/base.html +++ b/data/interfaces/default/base.html @@ -31,15 +31,15 @@
    % if not headphones.CURRENT_VERSION: -
    - You're running an unknown version of Headphones. Update or - Close -
    - % elif headphones.CURRENT_VERSION != headphones.LATEST_VERSION and headphones.INSTALL_TYPE != 'win': -
    - A newer version is available. You're ${headphones.COMMITS_BEHIND} commits behind. Update or Close -
    - % endif +
    + You're running an unknown version of Headphones. Update or + Close +
    + % elif headphones.CURRENT_VERSION != headphones.LATEST_VERSION and headphones.COMMITS_BEHIND > 0 and headphones.INSTALL_TYPE != 'win': +
    + A newer version is available. You're ${headphones.COMMITS_BEHIND} commits behind. Update or Close +
    + % endif
    @@ -55,16 +55,16 @@
  • + + +
    @@ -89,6 +89,9 @@ %if version.HEADPHONES_VERSION != 'master': (${version.HEADPHONES_VERSION}) %endif + %if headphones.GIT_BRANCH != 'master': + (${headphones.GIT_BRANCH}) + %endif Back to top diff --git a/data/interfaces/default/config.html b/data/interfaces/default/config.html index 486f79f7..820e729d 100644 --- a/data/interfaces/default/config.html +++ b/data/interfaces/default/config.html @@ -200,6 +200,11 @@ Folder your Download program watches for Torrents +
    + + + Allow Headphones to open magnet links +
    @@ -250,6 +255,12 @@
    +
    + + NZBs + Torrents + No Preference +
    @@ -486,7 +497,7 @@
    - Results without these words in the title will be filtered out + Results without these words in the title will be filtered out. You can use OR: 'flac OR lossless OR alac, vinyl'
    @@ -494,7 +505,12 @@
    Post-Processing
    - + +
    +
    + +
    +
    @@ -528,6 +544,23 @@
    '+data[i].title+''+(data[i].size / (1024*1024)).toFixed(2)+' MB'+data[i].provider+''+data[i].kind+'
    + @@ -54,6 +55,7 @@ + %endfor diff --git a/data/interfaces/default/images/trashcan.png b/data/interfaces/default/images/trashcan.png new file mode 100644 index 00000000..de10cbda Binary files /dev/null and b/data/interfaces/default/images/trashcan.png differ diff --git a/data/interfaces/default/manage.html b/data/interfaces/default/manage.html index c0c67d25..c2c0206c 100644 --- a/data/interfaces/default/manage.html +++ b/data/interfaces/default/manage.html @@ -122,7 +122,6 @@ + +
    +
    + + +
    +
    + + + +
    +
    + + +
    +
    + + diff --git a/headphones/__init__.py b/headphones/__init__.py index 8a4637bc..ed0ed0d3 100644 --- a/headphones/__init__.py +++ b/headphones/__init__.py @@ -82,7 +82,8 @@ API_KEY = None GIT_PATH = None GIT_USER = None -GIT_BRANCH =None +GIT_BRANCH = None +DO_NOT_OVERRIDE_GIT_BRANCH = False INSTALL_TYPE = None CURRENT_VERSION = None LATEST_VERSION = None @@ -114,6 +115,7 @@ ADD_ALBUM_ART = False ALBUM_ART_FORMAT = None EMBED_ALBUM_ART = False EMBED_LYRICS = False +REPLACE_EXISTING_FOLDERS = False NZB_DOWNLOADER = None # 0: sabnzbd, 1: nzbget, 2: blackhole TORRENT_DOWNLOADER = None # 0: blackhole, 1: transmission, 2: utorrent DOWNLOAD_DIR = None @@ -125,6 +127,8 @@ EXTRAS = None AUTOWANT_UPCOMING = False AUTOWANT_ALL = False KEEP_TORRENT_FILES = False +PREFER_TORRENTS = None # 0: nzbs, 1: torrents, 2: no preference +OPEN_MAGNET_LINKS = False SEARCH_INTERVAL = 360 LIBRARYSCAN = False @@ -178,7 +182,7 @@ REQUIRED_WORDS = None LASTFM_USERNAME = None -LOSSY_MEDIA_FORMATS = ["mp3", "aac", "ogg", "ape", "m4a"] +LOSSY_MEDIA_FORMATS = ["mp3", "aac", "ogg", "ape", "m4a", "asf", "wma"] LOSSLESS_MEDIA_FORMATS = ["flac"] MEDIA_FORMATS = LOSSY_MEDIA_FORMATS + LOSSLESS_MEDIA_FORMATS @@ -218,7 +222,13 @@ ENCODEROUTPUTFORMAT = None ENCODERQUALITY = None ENCODERVBRCBR = None ENCODERLOSSLESS = False +ENCODER_MULTICORE = False +ENCODER_MULTICORE_COUNT = 0 DELETE_LOSSLESS_FILES = False +GROWL_ENABLED = True +GROWL_HOST = None +GROWL_PASSWORD = None +GROWL_ONSNATCH = True PROWL_ENABLED = True PROWL_PRIORITY = 1 PROWL_KEYS = None @@ -229,6 +239,8 @@ XBMC_USERNAME = None XBMC_PASSWORD = None XBMC_UPDATE = False XBMC_NOTIFY = False +LMS_ENABLED = False +LMS_HOST = None PLEX_ENABLED = False PLEX_SERVER_HOST = None PLEX_CLIENT_HOST = None @@ -248,6 +260,7 @@ PUSHOVER_ENABLED = True PUSHOVER_PRIORITY = 1 PUSHOVER_KEYS = None PUSHOVER_ONSNATCH = True +PUSHOVER_APITOKEN = None PUSHBULLET_ENABLED = True PUSHBULLET_APIKEY = None PUSHBULLET_DEVICEID = None @@ -296,7 +309,7 @@ def check_setting_int(config, cfg_name, item_name, def_val): except: config[cfg_name] = {} config[cfg_name][item_name] = my_val - logger.debug(item_name + " -> " + str(my_val)) + logger.debug("%s -> %s", item_name, my_val) return my_val ################################################################################ @@ -313,10 +326,7 @@ def check_setting_str(config, cfg_name, item_name, def_val, log=True): config[cfg_name] = {} config[cfg_name][item_name] = my_val - if log: - logger.debug(item_name + " -> " + my_val) - else: - logger.debug(item_name + " -> ******") + logger.debug("%s -> %s", item_name, my_val if log else "******") return my_val def initialize(): @@ -324,11 +334,11 @@ def initialize(): with INIT_LOCK: global __INITIALIZED__, FULL_PATH, PROG_DIR, VERBOSE, DAEMON, SYS_PLATFORM, DATA_DIR, CONFIG_FILE, CFG, CONFIG_VERSION, LOG_DIR, CACHE_DIR, \ - HTTP_PORT, HTTP_HOST, HTTP_USERNAME, HTTP_PASSWORD, HTTP_ROOT, HTTP_PROXY, LAUNCH_BROWSER, API_ENABLED, API_KEY, GIT_PATH, GIT_USER, GIT_BRANCH, \ + HTTP_PORT, HTTP_HOST, HTTP_USERNAME, HTTP_PASSWORD, HTTP_ROOT, HTTP_PROXY, LAUNCH_BROWSER, API_ENABLED, API_KEY, GIT_PATH, GIT_USER, GIT_BRANCH, DO_NOT_OVERRIDE_GIT_BRANCH, \ CURRENT_VERSION, LATEST_VERSION, CHECK_GITHUB, CHECK_GITHUB_ON_STARTUP, CHECK_GITHUB_INTERVAL, MUSIC_DIR, DESTINATION_DIR, \ LOSSLESS_DESTINATION_DIR, PREFERRED_QUALITY, PREFERRED_BITRATE, DETECT_BITRATE, ADD_ARTISTS, CORRECT_METADATA, MOVE_FILES, \ - RENAME_FILES, FOLDER_FORMAT, FILE_FORMAT, FILE_UNDERSCORES, CLEANUP_FILES, INCLUDE_EXTRAS, EXTRAS, AUTOWANT_UPCOMING, AUTOWANT_ALL, KEEP_TORRENT_FILES, \ - ADD_ALBUM_ART, ALBUM_ART_FORMAT, EMBED_ALBUM_ART, EMBED_LYRICS, DOWNLOAD_DIR, BLACKHOLE, BLACKHOLE_DIR, USENET_RETENTION, SEARCH_INTERVAL, \ + RENAME_FILES, FOLDER_FORMAT, FILE_FORMAT, FILE_UNDERSCORES, CLEANUP_FILES, INCLUDE_EXTRAS, EXTRAS, AUTOWANT_UPCOMING, AUTOWANT_ALL, KEEP_TORRENT_FILES, PREFER_TORRENTS, OPEN_MAGNET_LINKS, \ + ADD_ALBUM_ART, ALBUM_ART_FORMAT, EMBED_ALBUM_ART, EMBED_LYRICS, REPLACE_EXISTING_FOLDERS, DOWNLOAD_DIR, BLACKHOLE, BLACKHOLE_DIR, USENET_RETENTION, SEARCH_INTERVAL, \ TORRENTBLACKHOLE_DIR, NUMBEROFSEEDERS, ISOHUNT, KAT, PIRATEBAY, PIRATEBAY_PROXY_URL, MININOVA, WAFFLES, WAFFLES_UID, WAFFLES_PASSKEY, \ RUTRACKER, RUTRACKER_USER, RUTRACKER_PASSWORD, WHATCD, WHATCD_USERNAME, WHATCD_PASSWORD, DOWNLOAD_TORRENT_DIR, \ LIBRARYSCAN, LIBRARYSCAN_INTERVAL, DOWNLOAD_SCAN_INTERVAL, UPDATE_DB_INTERVAL, MB_IGNORE_AGE, SAB_HOST, SAB_USERNAME, SAB_PASSWORD, SAB_APIKEY, SAB_CATEGORY, \ @@ -337,12 +347,12 @@ def initialize(): NZBSORG, NZBSORG_UID, NZBSORG_HASH, NZBSRUS, NZBSRUS_UID, NZBSRUS_APIKEY, OMGWTFNZBS, OMGWTFNZBS_UID, OMGWTFNZBS_APIKEY, \ NZB_DOWNLOADER, TORRENT_DOWNLOADER, PREFERRED_WORDS, REQUIRED_WORDS, IGNORED_WORDS, LASTFM_USERNAME, \ INTERFACE, FOLDER_PERMISSIONS, FILE_PERMISSIONS, ENCODERFOLDER, ENCODER_PATH, ENCODER, XLDPROFILE, BITRATE, SAMPLINGFREQUENCY, \ - MUSIC_ENCODER, ADVANCEDENCODER, ENCODEROUTPUTFORMAT, ENCODERQUALITY, ENCODERVBRCBR, ENCODERLOSSLESS, DELETE_LOSSLESS_FILES, \ - PROWL_ENABLED, PROWL_PRIORITY, PROWL_KEYS, PROWL_ONSNATCH, PUSHOVER_ENABLED, PUSHOVER_PRIORITY, PUSHOVER_KEYS, PUSHOVER_ONSNATCH, MIRRORLIST, \ + MUSIC_ENCODER, ADVANCEDENCODER, ENCODEROUTPUTFORMAT, ENCODERQUALITY, ENCODERVBRCBR, ENCODERLOSSLESS, ENCODER_MULTICORE, ENCODER_MULTICORE_COUNT, DELETE_LOSSLESS_FILES, \ + GROWL_ENABLED, GROWL_HOST, GROWL_PASSWORD, GROWL_ONSNATCH, PROWL_ENABLED, PROWL_PRIORITY, PROWL_KEYS, PROWL_ONSNATCH, PUSHOVER_ENABLED, PUSHOVER_PRIORITY, PUSHOVER_KEYS, PUSHOVER_ONSNATCH, PUSHOVER_APITOKEN, MIRRORLIST, \ TWITTER_ENABLED, TWITTER_ONSNATCH, TWITTER_USERNAME, TWITTER_PASSWORD, TWITTER_PREFIX, \ PUSHBULLET_ENABLED, PUSHBULLET_APIKEY, PUSHBULLET_DEVICEID, PUSHBULLET_ONSNATCH, \ MIRROR, CUSTOMHOST, CUSTOMPORT, CUSTOMSLEEP, HPUSER, HPPASS, XBMC_ENABLED, XBMC_HOST, XBMC_USERNAME, XBMC_PASSWORD, XBMC_UPDATE, \ - XBMC_NOTIFY, NMA_ENABLED, NMA_APIKEY, NMA_PRIORITY, NMA_ONSNATCH, SYNOINDEX_ENABLED, ALBUM_COMPLETION_PCT, PREFERRED_BITRATE_HIGH_BUFFER, \ + XBMC_NOTIFY, LMS_ENABLED, LMS_HOST, NMA_ENABLED, NMA_APIKEY, NMA_PRIORITY, NMA_ONSNATCH, SYNOINDEX_ENABLED, ALBUM_COMPLETION_PCT, PREFERRED_BITRATE_HIGH_BUFFER, \ PREFERRED_BITRATE_LOW_BUFFER, PREFERRED_BITRATE_ALLOW_LOSSLESS, CACHE_SIZEMB, JOURNAL_MODE, UMASK, ENABLE_HTTPS, HTTPS_CERT, HTTPS_KEY, \ PLEX_ENABLED, PLEX_SERVER_HOST, PLEX_CLIENT_HOST, PLEX_USERNAME, PLEX_PASSWORD, PLEX_UPDATE, PLEX_NOTIFY, PUSHALOT_ENABLED, PUSHALOT_APIKEY, \ PUSHALOT_ONSNATCH, SONGKICK_ENABLED, SONGKICK_APIKEY, SONGKICK_LOCATION, SONGKICK_FILTER_ENABLED @@ -365,10 +375,12 @@ def initialize(): CheckSection('Waffles') CheckSection('Rutracker') CheckSection('What.cd') + CheckSection('Growl') CheckSection('Prowl') CheckSection('Pushover') CheckSection('PushBullet') CheckSection('XBMC') + CheckSection('LMS') CheckSection('Plex') CheckSection('NMA') CheckSection('Pushalot') @@ -402,6 +414,7 @@ def initialize(): GIT_PATH = check_setting_str(CFG, 'General', 'git_path', '') GIT_USER = check_setting_str(CFG, 'General', 'git_user', 'rembo10') GIT_BRANCH = check_setting_str(CFG, 'General', 'git_branch', 'master') + DO_NOT_OVERRIDE_GIT_BRANCH = check_setting_int(CFG, 'General', 'do_not_override_git_branch', 0) LOG_DIR = check_setting_str(CFG, 'General', 'log_dir', '') CACHE_DIR = check_setting_str(CFG, 'General', 'cache_dir', '') @@ -430,6 +443,7 @@ def initialize(): ALBUM_ART_FORMAT = check_setting_str(CFG, 'General', 'album_art_format', 'folder') EMBED_ALBUM_ART = bool(check_setting_int(CFG, 'General', 'embed_album_art', 0)) EMBED_LYRICS = bool(check_setting_int(CFG, 'General', 'embed_lyrics', 0)) + REPLACE_EXISTING_FOLDERS = bool(check_setting_int(CFG, 'General', 'replace_existing_folders', 0)) NZB_DOWNLOADER = check_setting_int(CFG, 'General', 'nzb_downloader', 0) TORRENT_DOWNLOADER = check_setting_int(CFG, 'General', 'torrent_downloader', 0) DOWNLOAD_DIR = check_setting_str(CFG, 'General', 'download_dir', '') @@ -441,6 +455,8 @@ def initialize(): AUTOWANT_UPCOMING = bool(check_setting_int(CFG, 'General', 'autowant_upcoming', 1)) AUTOWANT_ALL = bool(check_setting_int(CFG, 'General', 'autowant_all', 0)) KEEP_TORRENT_FILES = bool(check_setting_int(CFG, 'General', 'keep_torrent_files', 0)) + PREFER_TORRENTS = check_setting_int(CFG, 'General', 'prefer_torrents', 0) + OPEN_MAGNET_LINKS = bool(check_setting_int(CFG, 'General', 'open_magnet_links', 0)) SEARCH_INTERVAL = check_setting_int(CFG, 'General', 'search_interval', 1440) LIBRARYSCAN = bool(check_setting_int(CFG, 'General', 'libraryscan', 1)) @@ -534,8 +550,15 @@ def initialize(): ENCODERQUALITY = check_setting_int(CFG, 'General', 'encoderquality', 2) ENCODERVBRCBR = check_setting_str(CFG, 'General', 'encodervbrcbr', 'cbr') ENCODERLOSSLESS = bool(check_setting_int(CFG, 'General', 'encoderlossless', 1)) + ENCODER_MULTICORE = bool(check_setting_int(CFG, 'General', 'encoder_multicore', 0)) + ENCODER_MULTICORE_COUNT = max(0, check_setting_int(CFG, 'General', 'encoder_multicore_count', 0)) DELETE_LOSSLESS_FILES = bool(check_setting_int(CFG, 'General', 'delete_lossless_files', 1)) + GROWL_ENABLED = bool(check_setting_int(CFG, 'Growl', 'growl_enabled', 0)) + GROWL_HOST = check_setting_str(CFG, 'Growl', 'growl_host', '') + GROWL_PASSWORD = check_setting_str(CFG, 'Growl', 'growl_password', '') + GROWL_ONSNATCH = bool(check_setting_int(CFG, 'Growl', 'growl_onsnatch', 0)) + PROWL_ENABLED = bool(check_setting_int(CFG, 'Prowl', 'prowl_enabled', 0)) PROWL_KEYS = check_setting_str(CFG, 'Prowl', 'prowl_keys', '') PROWL_ONSNATCH = bool(check_setting_int(CFG, 'Prowl', 'prowl_onsnatch', 0)) @@ -548,6 +571,9 @@ def initialize(): XBMC_UPDATE = bool(check_setting_int(CFG, 'XBMC', 'xbmc_update', 0)) XBMC_NOTIFY = bool(check_setting_int(CFG, 'XBMC', 'xbmc_notify', 0)) + LMS_ENABLED = bool(check_setting_int(CFG, 'LMS', 'lms_enabled', 0)) + LMS_HOST = check_setting_str(CFG, 'LMS', 'lms_host', '') + PLEX_ENABLED = bool(check_setting_int(CFG, 'Plex', 'plex_enabled', 0)) PLEX_SERVER_HOST = check_setting_str(CFG, 'Plex', 'plex_server_host', '') PLEX_CLIENT_HOST = check_setting_str(CFG, 'Plex', 'plex_client_host', '') @@ -571,6 +597,7 @@ def initialize(): PUSHOVER_KEYS = check_setting_str(CFG, 'Pushover', 'pushover_keys', '') PUSHOVER_ONSNATCH = bool(check_setting_int(CFG, 'Pushover', 'pushover_onsnatch', 0)) PUSHOVER_PRIORITY = check_setting_int(CFG, 'Pushover', 'pushover_priority', 0) + PUSHOVER_APITOKEN = check_setting_str(CFG, 'Pushover', 'pushover_apitoken', '') PUSHBULLET_ENABLED = bool(check_setting_int(CFG, 'PushBullet', 'pushbullet_enabled', 0)) PUSHBULLET_APIKEY = check_setting_str(CFG, 'PushBullet', 'pushbullet_apikey', '') @@ -668,10 +695,10 @@ def initialize(): os.makedirs(LOG_DIR) except OSError: if VERBOSE: - print 'Unable to create the log directory. Logging to screen only.' + sys.stderr.write('Unable to create the log directory. Logging to screen only.\n') # Start the logger, silence console logging if we need to - logger.headphones_log.initLogger(verbose=VERBOSE) + logger.initLogger(verbose=VERBOSE) if not CACHE_DIR: # Put the cache dir in the data dir for now @@ -680,7 +707,7 @@ def initialize(): try: os.makedirs(CACHE_DIR) except OSError: - logger.error('Could not create cache dir. Check permissions of datadir: ' + DATA_DIR) + logger.error('Could not create cache dir. Check permissions of datadir: %s', DATA_DIR) # Sanity check for search interval. Set it to at least 6 hours if SEARCH_INTERVAL < 360: @@ -692,17 +719,18 @@ def initialize(): try: dbcheck() except Exception, e: - logger.error("Can't connect to the database: %s" % e) + logger.error("Can't connect to the database: %s", e) # Get the currently installed version - returns None, 'win32' or the git hash # Also sets INSTALL_TYPE variable to 'win', 'git' or 'source' - CURRENT_VERSION = versioncheck.getVersion() + CURRENT_VERSION, GIT_BRANCH = versioncheck.getVersion() # Check for new versions if CHECK_GITHUB_ON_STARTUP: try: LATEST_VERSION = versioncheck.checkGithub() except: + logger.exception("Unhandled exception") LATEST_VERSION = CURRENT_VERSION else: LATEST_VERSION = CURRENT_VERSION @@ -729,7 +757,7 @@ def daemonize(): if pid != 0: sys.exit(0) except OSError, e: - raise RuntimeError("1st fork failed: %s [%d]" % (e.strerror, e.errno)) + raise RuntimeError("1st fork failed: %s [%d]", e.strerror, e.errno) os.setsid() @@ -743,7 +771,7 @@ def daemonize(): if pid != 0: sys.exit(0) except OSError, e: - raise RuntimeError("2nd fork failed: %s [%d]" % (e.strerror, e.errno)) + raise RuntimeError("2nd fork failed: %s [%d]", e.strerror, e.errno) dev_null = file('/dev/null', 'r') os.dup2(dev_null.fileno(), sys.stdin.fileno()) @@ -756,12 +784,13 @@ def daemonize(): os.dup2(so.fileno(), sys.stdout.fileno()) os.dup2(se.fileno(), sys.stderr.fileno()) - pid = str(os.getpid()) - logger.info('Daemonized to PID: %s' % pid) + pid = os.getpid() + logger.info('Daemonized to PID: %d', pid) if CREATEPID: - logger.info("Writing PID " + pid + " to " + str(PIDFILE)) - file(PIDFILE, 'w').write("%s\n" % pid) + logger.info("Writing PID %d to %s", pid, PIDFILE) + with file(PIDFILE, 'w') as fp: + fp.write("%s\n" % pid) def launch_browser(host, port, root): @@ -776,7 +805,7 @@ def launch_browser(host, port, root): try: webbrowser.open('%s://%s:%i%s' % (protocol, host, port, root)) except Exception, e: - logger.error('Could not launch browser: %s' % e) + logger.error('Could not launch browser: %s', e) def config_write(): @@ -802,6 +831,7 @@ def config_write(): new_config['General']['git_path'] = GIT_PATH new_config['General']['git_user'] = GIT_USER new_config['General']['git_branch'] = GIT_BRANCH + new_config['General']['do_not_override_git_branch'] = int(DO_NOT_OVERRIDE_GIT_BRANCH) new_config['General']['check_github'] = int(CHECK_GITHUB) new_config['General']['check_github_on_startup'] = int(CHECK_GITHUB_ON_STARTUP) @@ -828,6 +858,7 @@ def config_write(): new_config['General']['album_art_format'] = ALBUM_ART_FORMAT new_config['General']['embed_album_art'] = int(EMBED_ALBUM_ART) new_config['General']['embed_lyrics'] = int(EMBED_LYRICS) + new_config['General']['replace_existing_folders'] = int(REPLACE_EXISTING_FOLDERS) new_config['General']['nzb_downloader'] = NZB_DOWNLOADER new_config['General']['torrent_downloader'] = TORRENT_DOWNLOADER new_config['General']['download_dir'] = DOWNLOAD_DIR @@ -838,6 +869,8 @@ def config_write(): new_config['General']['autowant_upcoming'] = int(AUTOWANT_UPCOMING) new_config['General']['autowant_all'] = int(AUTOWANT_ALL) new_config['General']['keep_torrent_files'] = int(KEEP_TORRENT_FILES) + new_config['General']['prefer_torrents'] = PREFER_TORRENTS + new_config['General']['open_magnet_links'] = OPEN_MAGNET_LINKS new_config['General']['numberofseeders'] = NUMBEROFSEEDERS new_config['General']['torrentblackhole_dir'] = TORRENTBLACKHOLE_DIR @@ -928,6 +961,12 @@ def config_write(): new_config['General']['ignored_words'] = IGNORED_WORDS new_config['General']['required_words'] = REQUIRED_WORDS + new_config['Growl'] = {} + new_config['Growl']['growl_enabled'] = int(GROWL_ENABLED) + new_config['Growl']['growl_host'] = GROWL_HOST + new_config['Growl']['growl_password'] = GROWL_PASSWORD + new_config['Growl']['growl_onsnatch'] = int(GROWL_ONSNATCH) + new_config['Prowl'] = {} new_config['Prowl']['prowl_enabled'] = int(PROWL_ENABLED) new_config['Prowl']['prowl_keys'] = PROWL_KEYS @@ -942,6 +981,10 @@ def config_write(): new_config['XBMC']['xbmc_update'] = int(XBMC_UPDATE) new_config['XBMC']['xbmc_notify'] = int(XBMC_NOTIFY) + new_config['LMS'] = {} + new_config['LMS']['lms_enabled'] = int(LMS_ENABLED) + new_config['LMS']['lms_host'] = LMS_HOST + new_config['Plex'] = {} new_config['Plex']['plex_enabled'] = int(PLEX_ENABLED) new_config['Plex']['plex_server_host'] = PLEX_SERVER_HOST @@ -967,6 +1010,7 @@ def config_write(): new_config['Pushover']['pushover_keys'] = PUSHOVER_KEYS new_config['Pushover']['pushover_onsnatch'] = int(PUSHOVER_ONSNATCH) new_config['Pushover']['pushover_priority'] = int(PUSHOVER_PRIORITY) + new_config['Pushover']['pushover_apitoken'] = PUSHOVER_APITOKEN new_config['PushBullet'] = {} new_config['PushBullet']['pushbullet_enabled'] = int(PUSHBULLET_ENABLED) @@ -1006,6 +1050,8 @@ def config_write(): new_config['General']['encoderquality'] = ENCODERQUALITY new_config['General']['encodervbrcbr'] = ENCODERVBRCBR new_config['General']['encoderlossless'] = int(ENCODERLOSSLESS) + new_config['General']['encoder_multicore'] = int(ENCODER_MULTICORE) + new_config['General']['encoder_multicore_count'] = int(ENCODER_MULTICORE_COUNT) new_config['General']['delete_lossless_files'] = int(DELETE_LOSSLESS_FILES) new_config['General']['mirror'] = MIRROR @@ -1048,7 +1094,7 @@ def start(): def sig_handler(signum=None, frame=None): if type(signum) != type(None): - logger.info("Signal %i caught, saving and exiting..." % int(signum)) + logger.info("Signal %i caught, saving and exiting...", signum) shutdown() def dbcheck(): @@ -1267,10 +1313,10 @@ def shutdown(restart=False, update=False): try: versioncheck.update() except Exception, e: - logger.warn('Headphones failed to update: %s. Restarting.' % e) + logger.warn('Headphones failed to update: %s. Restarting.', e) if CREATEPID : - logger.info ('Removing pidfile %s' % PIDFILE) + logger.info ('Removing pidfile %s', PIDFILE) os.remove(PIDFILE) if restart: @@ -1279,7 +1325,7 @@ def shutdown(restart=False, update=False): popen_list += ARGS if '--nolaunch' not in popen_list: popen_list += ['--nolaunch'] - logger.info('Restarting Headphones with ' + str(popen_list)) + logger.info('Restarting Headphones with %s', popen_list) subprocess.Popen(popen_list, cwd=os.getcwd()) os._exit(0) diff --git a/headphones/albumart.py b/headphones/albumart.py index ee437bb2..daf137b0 100644 --- a/headphones/albumart.py +++ b/headphones/albumart.py @@ -13,39 +13,30 @@ # You should have received a copy of the GNU General Public License # along with Headphones. If not, see . -import urllib2 -from headphones import db +from headphones import request, db def getAlbumArt(albumid): - myDB = db.DBConnection() asin = myDB.action('SELECT AlbumASIN from albums WHERE AlbumID=?', [albumid]).fetchone()[0] - - if not asin: - return None - - url = 'http://ec1.images-amazon.com/images/P/%s.01.LZZZZZZZ.jpg' % asin - - return url - + + if asin: + return 'http://ec1.images-amazon.com/images/P/%s.01.LZZZZZZZ.jpg' % asin + def getCachedArt(albumid): - from headphones import cache - + c = cache.Cache() - artwork_path = c.get_artwork_from_cache(AlbumID=albumid) - + if not artwork_path: - return None - + return + if artwork_path.startswith('http://'): - try: - artwork = urllib2.urlopen(artwork_path, timeout=20).read() - return artwork - except: - logger.warn("Unable to open url: " + artwork_path) - return None + artwork = request.request_content(artwork_path, timeout=20) + + if not artwork: + logger.warn("Unable to open url: %s", artwork_path) + return else: - artwork = open(artwork_path, "r").read() - return artwork + with open(artwork_path, "r") as fp: + return fp.read() diff --git a/headphones/api.py b/headphones/api.py index 7754f3f6..03888a26 100644 --- a/headphones/api.py +++ b/headphones/api.py @@ -24,7 +24,8 @@ import copy cmd_list = [ 'getIndex', 'getArtist', 'getAlbum', 'getUpcoming', 'getWanted', 'getSimilar', 'getHistory', 'getLogs', 'findArtist', 'findAlbum', 'addArtist', 'delArtist', 'pauseArtist', 'resumeArtist', 'refreshArtist', 'addAlbum', 'queueAlbum', 'unqueueAlbum', 'forceSearch', 'forceProcess', 'getVersion', 'checkGithub', - 'shutdown', 'restart', 'update', 'getArtistArt', 'getAlbumArt', 'getArtistInfo', 'getAlbumInfo', 'getArtistThumb', 'getAlbumThumb'] + 'shutdown', 'restart', 'update', 'getArtistArt', 'getAlbumArt', 'getArtistInfo', 'getAlbumInfo', + 'getArtistThumb', 'getAlbumThumb', 'choose_specific_download', 'download_specific_release'] class Api(object): @@ -78,8 +79,8 @@ class Api(object): def fetchData(self): - if self.data == 'OK': - logger.info('Recieved API command: ' + self.cmd) + if self.data == 'OK': + logger.info('Recieved API command: %s', self.cmd) methodToCall = getattr(self, "_" + self.cmd) result = methodToCall(**self.kwargs) if 'callback' not in self.kwargs: @@ -306,7 +307,10 @@ class Api(object): searcher.searchforalbum() def _forceProcess(self, **kwargs): - postprocessor.forcePostProcess() + self.dir = None + if 'dir' in kwargs: + self.dir = kwargs['dir'] + postprocessor.forcePostProcess(self.dir) def _getVersion(self, **kwargs): self.data = { @@ -389,3 +393,65 @@ class Api(object): self.id = kwargs['id'] self.data = cache.getThumb(AlbumID=self.id) + + def _choose_specific_download(self, **kwargs): + + if 'id' not in kwargs: + self.data = 'Missing parameter: id' + return + else: + self.id = kwargs['id'] + + results = searcher.searchforalbum(self.id, choose_specific_download=True) + + results_as_dicts = [] + + for result in results: + + result_dict = { + 'title':result[0], + 'size':result[1], + 'url':result[2], + 'provider':result[3], + 'kind':result[4] + } + results_as_dicts.append(result_dict) + + self.data = results_as_dicts + + def _download_specific_release(self, **kwargs): + + expected_kwargs =['id', 'title','size','url','provider','kind'] + + for kwarg in expected_kwargs: + if kwarg not in kwargs: + self.data = 'Missing parameter: ' + kwarg + return self.data + + title = kwargs['title'] + size = kwargs['size'] + url = kwargs['url'] + provider = kwargs['provider'] + kind = kwargs['kind'] + id = kwargs['id'] + + for kwarg in expected_kwargs: + del kwargs[kwarg] + + # Handle situations where the torrent url contains arguments that are parsed + if kwargs: + import urllib, urllib2 + url = urllib2.quote(url, safe=":?/=&") + '&' + urllib.urlencode(kwargs) + + try: + result = [(title,int(size),url,provider,kind)] + except ValueError: + result = [(title,float(size),url,provider,kind)] + + logger.info(u"Making sure we can download the chosen result") + (data, bestqual) = searcher.preprocess(result) + + if data and bestqual: + myDB = db.DBConnection() + album = myDB.action('SELECT * from albums WHERE AlbumID=?', [id]).fetchone() + searcher.send_to_downloader(data, bestqual, album) diff --git a/headphones/cache.py b/headphones/cache.py index 78cd0c47..a762fd20 100644 --- a/headphones/cache.py +++ b/headphones/cache.py @@ -14,53 +14,52 @@ # along with Headphones. If not, see . import os -import glob, urllib, urllib2 - -import lib.simplejson as simplejson - +import glob +import urllib import headphones -from headphones import db, helpers, logger + +from headphones import db, helpers, logger, lastfm, request lastfm_apikey = "690e1ed3bc00bc91804cd8f7fe5ed6d4" class Cache(object): """ - This class deals with getting, storing and serving up artwork (album + This class deals with getting, storing and serving up artwork (album art, artist images, etc) and info/descriptions (album info, artist descrptions) - to and from the cache folder. This can be called from within a web interface, + to and from the cache folder. This can be called from within a web interface, for example, using the helper functions getInfo(id) and getArtwork(id), to utilize the cached images rather than having to retrieve them every time the page is reloaded. - + So you can call cache.getArtwork(id) which will return an absolute path to the image file on the local machine, or if the cache directory doesn't exist, or can not be written to, it will return a url to the image. - + Call cache.getInfo(id) to grab the artist/album info; will return the text description - + The basic format for art in the cache is .. and for info it is ..txt """ - + path_to_art_cache = os.path.join(headphones.CACHE_DIR, 'artwork') - + id = None id_type = None # 'artist' or 'album' - set automatically depending on whether ArtistID or AlbumID is passed query_type = None # 'artwork','thumb' or 'info' - set automatically - + artwork_files = [] thumb_files = [] - + artwork_errors = False artwork_url = None - + thumb_errors = False thumb_url = None - + info_summary = None info_content = None - + def __init__(self): - + pass def _findfilesstartingwith(self,pattern,folder): @@ -70,7 +69,7 @@ class Cache(object): if fname.startswith(pattern): files.append(os.path.join(folder,fname)) return files - + def _exists(self, type): self.artwork_files = [] self.thumb_files = [] @@ -93,53 +92,53 @@ class Cache(object): # There's probably a better way to do this split_date = date.split('-') days_old = int(split_date[0])*365 + int(split_date[1])*30 + int(split_date[2]) - + return days_old - - + + def _is_current(self, filename=None, date=None): - + if filename: base_filename = os.path.basename(filename) date = base_filename.split('.')[1] - + # Calculate how old the cached file is based on todays date & file date stamp # helpers.today() returns todays date in yyyy-mm-dd format if self._get_age(helpers.today()) - self._get_age(date) < 30: return True else: return False - + def _get_thumb_url(self, data): - + thumb_url = None - + try: images = data[self.id_type]['image'] except KeyError: return None - + for image in images: if image['size'] == 'medium': thumb_url = image['#text'] break - + return thumb_url - + def get_artwork_from_cache(self, ArtistID=None, AlbumID=None): ''' Pass a musicbrainz id to this function (either ArtistID or AlbumID) ''' - + self.query_type = 'artwork' - + if ArtistID: self.id = ArtistID self.id_type = 'artist' else: self.id = AlbumID self.id_type = 'album' - + if self._exists('artwork') and self._is_current(filename=self.artwork_files[0]): return self.artwork_files[0] else: @@ -151,21 +150,21 @@ class Cache(object): return self.artwork_files[0] else: return None - + def get_thumb_from_cache(self, ArtistID=None, AlbumID=None): ''' Pass a musicbrainz id to this function (either ArtistID or AlbumID) ''' - + self.query_type = 'thumb' - + if ArtistID: self.id = ArtistID self.id_type = 'artist' else: self.id = AlbumID self.id_type = 'album' - + if self._exists('thumb') and self._is_current(filename=self.thumb_files[0]): return self.thumb_files[0] else: @@ -177,13 +176,13 @@ class Cache(object): return self.thumb_files[0] else: return None - + def get_info_from_cache(self, ArtistID=None, AlbumID=None): - + self.query_type = 'info' myDB = db.DBConnection() - - if ArtistID: + + if ArtistID: self.id = ArtistID self.id_type = 'artist' db_info = myDB.action('SELECT Summary, Content, LastUpdated FROM descriptions WHERE ArtistID=?', [self.id]).fetchone() @@ -193,7 +192,7 @@ class Cache(object): db_info = myDB.action('SELECT Summary, Content, LastUpdated FROM descriptions WHERE ReleaseGroupID=?', [self.id]).fetchone() if not db_info or not db_info['LastUpdated'] or not self._is_current(date=db_info['LastUpdated']): - + self._update_cache() info_dict = { 'Summary' : self.info_summary, 'Content' : self.info_content } return info_dict @@ -201,188 +200,114 @@ class Cache(object): else: info_dict = { 'Summary' : db_info['Summary'], 'Content' : db_info['Content'] } return info_dict - + def get_image_links(self, ArtistID=None, AlbumID=None): ''' Here we're just going to open up the last.fm url, grab the image links and return them Won't save any image urls, or save the artwork in the cache. Useful for search results, etc. ''' if ArtistID: - - self.id_type = 'artist' - - params = { "method": "artist.getInfo", - "api_key": lastfm_apikey, - "mbid": ArtistID, - "format": "json" - } - - url = "http://ws.audioscrobbler.com/2.0/?" + urllib.urlencode(params) - logger.debug('Retrieving artist information from: ' + url) - - try: - result = urllib2.urlopen(url, timeout=20).read() - except: - logger.warn('Could not open url: ' + url) - return - - if result: - - try: - data = simplejson.JSONDecoder().decode(result) - except: - logger.warn('Could not parse data from url: ' + url) - return - try: - image_url = data['artist']['image'][-1]['#text'] - except Exception: - logger.debug('No artist image found on url: ' + url) - image_url = None - - thumb_url = self._get_thumb_url(data) - if not thumb_url: - logger.debug('No artist thumbnail image found on url: ' + url) - - else: - - self.id_type = 'album' - - params = { "method": "album.getInfo", - "api_key": lastfm_apikey, - "mbid": AlbumID, - "format": "json" - } - - url = "http://ws.audioscrobbler.com/2.0/?" + urllib.urlencode(params) - logger.debug('Retrieving album information from: ' + url) - - try: - result = urllib2.urlopen(url, timeout=20).read() - except: - logger.warn('Could not open url: ' + url) + self.id_type = 'artist' + data = lastfm.request_lastfm("artist.getinfo", mbid=ArtistID, api_key=lastfm_apikey) + + if not data: return - - if result: - - try: - data = simplejson.JSONDecoder().decode(result) - except: - logger.warn('Could not parse data from url: ' + url) - return - - try: - image_url = data['artist']['image'][-1]['#text'] - except Exception: - logger.debug('No artist image found on url: ' + url) - image_url = None - - thumb_url = self._get_thumb_url(data) - - if not thumb_url: - logger.debug('No artist thumbnail image found on url: ' + url) - - image_dict = {'artwork' : image_url, 'thumbnail' : thumb_url } - return image_dict - + + try: + image_url = data['artist']['image'][-1]['#text'] + except Exception: + logger.debug('No artist image found') + image_url = None + + thumb_url = self._get_thumb_url(data) + if not thumb_url: + logger.debug('No artist thumbnail image found') + + else: + + self.id_type = 'album' + data = lastfm.request_lastfm("album.getinfo", mbid=AlbumID, api_key=lastfm_apikey) + + if not data: + return + + try: + image_url = data['artist']['image'][-1]['#text'] + except Exception: + logger.debug('No artist image found') + image_url = None + + thumb_url = self._get_thumb_url(data) + + if not thumb_url: + logger.debug('No artist thumbnail image found') + + return {'artwork' : image_url, 'thumbnail' : thumb_url } + def _update_cache(self): ''' Since we call the same url for both info and artwork, we'll update both at the same time ''' myDB = db.DBConnection() - + # Since lastfm uses release ids rather than release group ids for albums, we have to do a artist + album search for albums if self.id_type == 'artist': - - params = { "method": "artist.getInfo", - "api_key": lastfm_apikey, - "mbid": self.id, - "format": "json" - } - - url = "http://ws.audioscrobbler.com/2.0/?" + urllib.urlencode(params) - logger.debug('Retrieving artist information from: ' + url) - - try: - result = urllib2.urlopen(url, timeout=20).read() - except: - logger.warn('Could not open url: ' + url) + + data = lastfm.request_lastfm("artist.getinfo", mbid=self.id, api_key=lastfm_apikey) + + if not data: return - - if result: - - try: - data = simplejson.JSONDecoder().decode(result) - except: - logger.warn('Could not parse data from url: ' + url) - return - try: - self.info_summary = data['artist']['bio']['summary'] - except Exception: - logger.debug('No artist bio summary found on url: ' + url) - self.info_summary = None - try: - self.info_content = data['artist']['bio']['content'] - except Exception: - logger.debug('No artist bio found on url: ' + url) - self.info_content = None - try: - image_url = data['artist']['image'][-1]['#text'] - except Exception: - logger.debug('No artist image found on url: ' + url) - image_url = None - - thumb_url = self._get_thumb_url(data) - if not thumb_url: - logger.debug('No artist thumbnail image found on url: ' + url) - + + try: + self.info_summary = data['artist']['bio']['summary'] + except Exception: + logger.debug('No artist bio summary found') + self.info_summary = None + try: + self.info_content = data['artist']['bio']['content'] + except Exception: + logger.debug('No artist bio found') + self.info_content = None + try: + image_url = data['artist']['image'][-1]['#text'] + except Exception: + logger.debug('No artist image found') + image_url = None + + thumb_url = self._get_thumb_url(data) + if not thumb_url: + logger.debug('No artist thumbnail image found') + else: dbartist = myDB.action('SELECT ArtistName, AlbumTitle FROM albums WHERE AlbumID=?', [self.id]).fetchone() - - params = { "method": "album.getInfo", - "api_key": lastfm_apikey, - "artist": dbartist['ArtistName'].encode('utf-8'), - "album": dbartist['AlbumTitle'].encode('utf-8'), - "format": "json" - } - - url = "http://ws.audioscrobbler.com/2.0/?" + urllib.urlencode(params) - - logger.debug('Retrieving artist information from: ' + url) - try: - result = urllib2.urlopen(url, timeout=20).read() - except: - logger.warn('Could not open url: ' + url) - return - - if result: - try: - data = simplejson.JSONDecoder().decode(result) - except: - logger.warn('Could not parse data from url: ' + url) - return - try: - self.info_summary = data['album']['wiki']['summary'] - except Exception: - logger.debug('No album summary found from: ' + url) - self.info_summary = None - try: - self.info_content = data['album']['wiki']['content'] - except Exception: - logger.debug('No album infomation found from: ' + url) - self.info_content = None - try: - image_url = data['album']['image'][-1]['#text'] - except Exception: - logger.debug('No album image link found on url: ' + url) - image_url = None - - thumb_url = self._get_thumb_url(data) + data = lastfm.request_lastfm("album.getinfo", artist=dbartist['ArtistName'], album=dbartist['AlbumTitle'], api_key=lastfm_apikey) + + if not data: + return + + try: + self.info_summary = data['album']['wiki']['summary'] + except Exception: + logger.debug('No album summary found') + self.info_summary = None + try: + self.info_content = data['album']['wiki']['content'] + except Exception: + logger.debug('No album infomation found') + self.info_content = None + try: + image_url = data['album']['image'][-1]['#text'] + except Exception: + logger.debug('No album image link found') + image_url = None + + thumb_url = self._get_thumb_url(data) + + if not thumb_url: + logger.debug('No album thumbnail image found') - if not thumb_url: - logger.debug('No album thumbnail image found on url: ' + url) - #Save the content & summary to the database no matter what if we've opened up the url if self.id_type == 'artist': controlValueDict = {"ArtistID": self.id} @@ -392,139 +317,128 @@ class Cache(object): newValueDict = {"Summary": self.info_summary, "Content": self.info_content, "LastUpdated": helpers.today()} - + myDB.upsert("descriptions", newValueDict, controlValueDict) - + # Save the image URL to the database if image_url: if self.id_type == 'artist': myDB.action('UPDATE artists SET ArtworkURL=? WHERE ArtistID=?', [image_url, self.id]) else: myDB.action('UPDATE albums SET ArtworkURL=? WHERE AlbumID=?', [image_url, self.id]) - + # Save the thumb URL to the database if thumb_url: if self.id_type == 'artist': myDB.action('UPDATE artists SET ThumbURL=? WHERE ArtistID=?', [thumb_url, self.id]) else: myDB.action('UPDATE albums SET ThumbURL=? WHERE AlbumID=?', [thumb_url, self.id]) - + # Should we grab the artwork here if we're just grabbing thumbs or info?? Probably not since the files can be quite big if image_url and self.query_type == 'artwork': - try: - artwork = urllib2.urlopen(image_url, timeout=20).read() - except Exception, e: - logger.error('Unable to open url "' + image_url + '". Error: ' + str(e)) - artwork = None - + artwork = request.request_content(image_url, timeout=20) + if artwork: - # Make sure the artwork dir exists: if not os.path.isdir(self.path_to_art_cache): try: os.makedirs(self.path_to_art_cache) except Exception, e: - logger.error('Unable to create artwork cache dir. Error: ' + str(e)) + logger.error('Unable to create artwork cache dir. Error: %s', e) self.artwork_errors = True self.artwork_url = image_url - + #Delete the old stuff for artwork_file in self.artwork_files: try: os.remove(artwork_file) except: - logger.error('Error deleting file from the cache: ' + artwork_file) - + logger.error('Error deleting file from the cache: %s', artwork_file) + ext = os.path.splitext(image_url)[1] - + artwork_path = os.path.join(self.path_to_art_cache, self.id + '.' + helpers.today() + ext) try: f = open(artwork_path, 'wb') f.write(artwork) f.close() except Exception, e: - logger.error('Unable to write to the cache dir: ' + str(e)) + logger.error('Unable to write to the cache dir: %s', e) self.artwork_errors = True self.artwork_url = image_url - + # Grab the thumbnail as well if we're getting the full artwork (as long as it's missing/outdated if thumb_url and self.query_type in ['thumb','artwork'] and not (self.thumb_files and self._is_current(self.thumb_files[0])): - - try: - artwork = urllib2.urlopen(thumb_url, timeout=20).read() - except Exception, e: - logger.error('Unable to open url "' + thumb_url + '". Error: ' + str(e)) - artwork = None - + artwork = request.request_content(thumb_url, timeout=20) + if artwork: - # Make sure the artwork dir exists: if not os.path.isdir(self.path_to_art_cache): try: os.makedirs(self.path_to_art_cache) except Exception, e: - logger.error('Unable to create artwork cache dir. Error: ' + str(e)) + logger.error('Unable to create artwork cache dir. Error: %s' + e) self.thumb_errors = True self.thumb_url = thumb_url - + #Delete the old stuff for thumb_file in self.thumb_files: try: os.remove(thumb_file) except: - logger.error('Error deleting file from the cache: ' + thumb_file) - + logger.error('Error deleting file from the cache: %s', thumb_file) + ext = os.path.splitext(image_url)[1] - + thumb_path = os.path.join(self.path_to_art_cache, 'T_' + self.id + '.' + helpers.today() + ext) try: f = open(thumb_path, 'wb') f.write(artwork) f.close() except Exception, e: - logger.error('Unable to write to the cache dir: ' + str(e)) + logger.error('Unable to write to the cache dir: %s', e) self.thumb_errors = True self.thumb_url = image_url def getArtwork(ArtistID=None, AlbumID=None): - + c = Cache() artwork_path = c.get_artwork_from_cache(ArtistID, AlbumID) - + if not artwork_path: return None - + if artwork_path.startswith('http://'): return artwork_path else: artwork_file = os.path.basename(artwork_path) return "cache/artwork/" + artwork_file - + def getThumb(ArtistID=None, AlbumID=None): - + c = Cache() artwork_path = c.get_thumb_from_cache(ArtistID, AlbumID) - + if not artwork_path: return None - + if artwork_path.startswith('http://'): return artwork_path else: thumbnail_file = os.path.basename(artwork_path) return "cache/artwork/" + thumbnail_file - + def getInfo(ArtistID=None, AlbumID=None): - + c = Cache() - + info_dict = c.get_info_from_cache(ArtistID, AlbumID) - + return info_dict - + def getImageLinks(ArtistID=None, AlbumID=None): - + c = Cache() image_links = c.get_image_links(ArtistID, AlbumID) - + return image_links diff --git a/headphones/db.py b/headphones/db.py index c0b9bba3..7068ba0e 100644 --- a/headphones/db.py +++ b/headphones/db.py @@ -73,14 +73,14 @@ class DBConnection: break except sqlite3.OperationalError, e: if "unable to open database file" in e.message or "database is locked" in e.message: - logger.warn('Database Error: %s' % e) + logger.warn('Database Error: %s', e) attempt += 1 time.sleep(1) else: - logger.error('Database error: %s' % e) + logger.error('Database error: %s', e) raise except sqlite3.DatabaseError, e: - logger.error('Fatal Error executing %s :: %s' % (query, e)) + logger.error('Fatal Error executing %s :: %s', query, e) raise return sqlResult diff --git a/headphones/helpers.py b/headphones/helpers.py index 79ac9b85..438dc7d7 100644 --- a/headphones/helpers.py +++ b/headphones/helpers.py @@ -13,17 +13,25 @@ # You should have received a copy of the GNU General Public License # along with Headphones. If not, see . -import os, time -from operator import itemgetter +import os +import re +import time +import shutil import datetime -import re, shutil - import headphones -def multikeysort(items, columns): +from operator import itemgetter +from beets.mediafile import MediaFile, FileTypeError, UnreadableFileError +# Modified from https://github.com/Verrus/beets-plugin-featInTitle +RE_FEATURING = re.compile(r"[fF]t\.|[fF]eaturing|[fF]eat\.|\b[wW]ith\b|&|vs\.") + +RE_CD_ALBUM = re.compile(r"\(?((CD|disc)\s*[0-9]+)\)?", re.I) +RE_CD = re.compile(r"^(CD|dics)\s*[0-9]+$", re.I) + +def multikeysort(items, columns): comparers = [ ((itemgetter(col[1:].strip()), -1) if col.startswith('-') else (itemgetter(col.strip()), 1)) for col in columns] - + def comparer(left, right): for fn, mult in comparers: result = cmp(fn(left), fn(right)) @@ -31,22 +39,22 @@ def multikeysort(items, columns): return mult * result else: return 0 - + return sorted(items, cmp=comparer) - + def checked(variable): if variable: return 'Checked' else: return '' - + def radio(variable, pos): if variable == pos: return 'Checked' else: return '' - + def latinToAscii(unicrap): """ From couch potato @@ -87,7 +95,7 @@ def latinToAscii(unicrap): else: r += str(i) return r - + def convert_milliseconds(ms): seconds = ms/1000 @@ -98,7 +106,7 @@ def convert_milliseconds(ms): minutes = time.strftime("%M:%S", gmtime) return minutes - + def convert_seconds(s): gmtime = time.gmtime(s) @@ -108,30 +116,30 @@ def convert_seconds(s): minutes = time.strftime("%M:%S", gmtime) return minutes - + def today(): today = datetime.date.today() yyyymmdd = datetime.date.isoformat(today) return yyyymmdd - + def now(): now = datetime.datetime.now() return now.strftime("%Y-%m-%d %H:%M:%S") - + def get_age(date): try: split_date = date.split('-') except: return False - + try: days_old = int(split_date[0])*365 + int(split_date[1])*30 + int(split_date[2]) except IndexError: days_old = False - + return days_old - + def bytes_to_mb(bytes): mb = int(bytes)/1048576 @@ -142,7 +150,7 @@ def mb_to_bytes(mb_str): result = re.search('^(\d+(?:\.\d+)?)\s?(?:mb)?', mb_str, flags=re.I) if result: return int(float(result.group(1))*1048576) - + def piratesize(size): split = size.split(" ") factor = float(split[0]) @@ -157,48 +165,155 @@ def piratesize(size): size = factor else: size = 0 - + return size def replace_all(text, dic): - + if not text: return '' - + for i, j in dic.iteritems(): text = text.replace(i, j) return text - + +def replace_illegal_chars(string, type="file"): + if type == "file": + string = re.sub('[\?"*:|<>/]', '_', string) + if type == "folder": + string = re.sub('[:\?<>"|]', '_', string) + + return string + def cleanName(string): pass1 = latinToAscii(string).lower() out_string = re.sub('[\.\-\/\!\@\#\$\%\^\&\*\(\)\+\-\"\'\,\;\:\[\]\{\}\<\>\=\_]', '', pass1).encode('utf-8') - + return out_string - + def cleanTitle(title): title = re.sub('[\.\-\/\_]', ' ', title).lower() - + # Strip out extra whitespace title = ' '.join(title.split()) - + title = title.title() - + return title - + +def split_path(f): + """ + Split a path into components, starting with the drive letter (if any). Given + a path, os.path.join(*split_path(f)) should be path equal to f. + """ + + components = [] + drive, path = os.path.splitdrive(f) + + # Stip the folder from the path, iterate until nothing is left + while True: + path, folder = os.path.split(path) + + if folder: + components.append(folder) + else: + if path: + components.append(path) + + break + + # Append the drive (if any) + if drive: + components.append(drive) + + # Reverse components + components.reverse() + + # Done + return components + +def expand_subfolders(f): + """ + Try to expand a given folder and search for subfolders containing media + files. This should work for discographies indexed per album in the same + root, possibly with folders per CD (if any). + + This algorithm will return nothing if the result is only one folder. In this + case, normal post processing will be better. + """ + + from headphones import logger + + # Find all folders with media files in them + media_folders = [] + + for root, dirs, files in os.walk(f): + for file in files: + extension = os.path.splitext(file)[1].lower()[1:] + + if extension in headphones.MEDIA_FORMATS: + if root not in media_folders: + media_folders.append(root) + + # Stop here if nothing found + if len(media_folders) == 0: + return + + # Split into path components + media_folders = [ split_path(media_folder) for media_folder in media_folders ] + + # Correct folder endings such as CD1 etc. + for index, media_folder in enumerate(media_folders): + if RE_CD.match(media_folder[-1]): + media_folders[index] = media_folders[index][:-1] + + # Verify the result by computing path depth relative to root. + path_depths = [ len(media_folder) for media_folder in media_folders ] + difference = max(path_depths) - min(path_depths) + + if difference > 0: + logger.info("Found %d media folders, but depth difference between lowest and deepest media folder is %d (expected zero). If this is a discography or a collection of albums, make sure albums are per folder" % (len(media_folders), difference)) + + # While already failed, advice the user what he could try. We assume the + # directory may contain separate CD's and maybe some extra's. The + # structure may look like X albums at same depth, and (one or more) + # extra folders with a higher depth. + extra_media_folders = [ media_folder[:min(path_depths)] for media_folder in media_folders if len(media_folder) > min(path_depths) ] + extra_media_folders = list(set([ os.path.join(*media_folder) for media_folder in extra_media_folders ])) + + logger.info("Please look at the following folder(s), since they cause the depth difference: %s" % extra_media_folders) + return + + # Convert back to paths and remove duplicates, which may be there after + # correcting the paths + media_folders = list(set([ os.path.join(*media_folder) for media_folder in media_folders ])) + + # Don't return a result if the number of subfolders is one. In this case, + # this algorithm will not improve processing and will likely interfere + # with other attempts such as MusicBrainz release group IDs. + if len(media_folders) == 1: + logger.debug("Did not expand subfolder, as it resulted in one folder.") + return + + logger.debug("Expanded subfolders in folder: " % media_folders) + return media_folders + def extract_data(s): + s = s.replace('_', ' ') + #headphones default format pattern = re.compile(r'(?P.*?)\s\-\s(?P.*?)\s\[(?P.*?)\]', re.VERBOSE) match = pattern.match(s) - + if match: name = match.group("name") album = match.group("album") year = match.group("year") return (name, album, year) - + #newzbin default format pattern = re.compile(r'(?P.*?)\s\-\s(?P.*?)\s\((?P\d+?\))', re.VERBOSE) match = pattern.match(s) @@ -207,9 +322,118 @@ def extract_data(s): album = match.group("album") year = match.group("year") return (name, album, year) + + #Gonna take a guess on this one - might be enough to search on mb + pat = re.compile(r"(?P.*?)\s*-\s*(?P[^\[(-]*)") + + match = pat.match(s) + if match: + name = match.group("name") + album = match.group("album") + year = None + return (name, album, year) + else: return (None, None, None) - + +def extract_metadata(f): + """ + Scan all files in the given directory and decide on an artist, album and + year based on the metadata. A decision is based on the number of different + artists, albums and years found in the media files. + """ + + from headphones import logger + + # Walk directory and scan all media files + results = [] + count = 0 + + for root, dirs, files in os.walk(f): + for file in files: + # Count the number of potential media files + extension = os.path.splitext(file)[1].lower()[1:] + + if extension in headphones.MEDIA_FORMATS: + count += 1 + + # Try to read the file info + try: + media_file = MediaFile(os.path.join(root, file)) + except FileTypeError, UnreadableFileError: + # Probably not a media file + continue + + # Append metadata to file + artist = media_file.albumartist or media_file.artist + album = media_file.album + year = media_file.year + + if artist and album and year: + results.append((artist.lower(), album.lower(), year)) + + # Verify results + if len(results) == 0: + logger.info("No metadata in media files found, ignoring") + return (None, None, None) + + # Require that some percentage of files have tags + count_ratio = 0.75 + + if count < (count_ratio * len(results)): + logger.info("Counted %d media files, but only %d have tags, ignoring" % (count, len(results))) + return (None, None, None) + + # Count distinct values + artists = list(set([ x[0] for x in results ])) + albums = list(set([ x[1] for x in results ])) + years = list(set([ x[2] for x in results ])) + + # Remove things such as CD2 from album names + if len(albums) > 1: + new_albums = list(albums) + + # Replace occurences of e.g. CD1 + for index, album in enumerate(new_albums): + if RE_CD_ALBUM.search(album): + old_album = new_albums[index] + new_albums[index] = RE_CD_ALBUM.sub("", album).strip() + + logger.debug("Stripped albumd number identifier: %s -> %s" % (old_album, new_albums[index])) + + # Remove duplicates + new_albums = list(set(new_albums)) + + # Safety check: if nothing has merged, then ignore the work. This can + # happen if only one CD of a multi part CD is processed. + if len(new_albums) < len(albums): + albums = new_albums + + # All files have the same metadata, so it's trivial + if len(artists) == 1 and len(albums) == 1: + return (artists[0], albums[0], years[0]) + + # (Lots of) different artists. Could be a featuring album, so test for this. + if len(artists) > 1 and len(albums) == 1: + split_artists = [ RE_FEATURING.split(artist) for artist in artists ] + featurings = [ len(split_artist) - 1 for split_artist in split_artists ] + logger.info("Album seem to feature %d different artists" % sum(featurings)) + + if sum(featurings) > 0: + # Find the artist of which the least splits have been generated. + # Ideally, this should be 0, which should be the album artist + # itself. + artist = split_artists[featurings.index(min(featurings))][0] + + # Done + return (artist, albums[0], years[0]) + + # Not sure what to do here. + logger.info("Found %d artists, %d albums and %d years in metadata, so ignoring" % (len(artists), len(albums), len(years))) + logger.debug("Artists: %s, Albums: %s, Years: %s" % (artists, albums, years)) + + return (None, None, None) + def extract_logline(s): # Default log format pattern = re.compile(r'(?P.*?)\s\-\s(?P.*?)\s*\:\:\s(?P.*?)\s\:\s(?P.*)', re.VERBOSE) @@ -222,26 +446,26 @@ def extract_logline(s): return (timestamp, level, thread, message) else: return None - + def extract_song_data(s): #headphones default format music_dir = headphones.MUSIC_DIR folder_format = headphones.FOLDER_FORMAT file_format = headphones.FILE_FORMAT - + full_format = os.path.join(headphones.MUSIC_DIR) pattern = re.compile(r'(?P.*?)\s\-\s(?P.*?)\s\[(?P.*?)\]', re.VERBOSE) match = pattern.match(s) - + if match: name = match.group("name") album = match.group("album") year = match.group("year") return (name, album, year) else: - logger.info("Couldn't parse " + s + " into a valid default format") - + logger.info("Couldn't parse %s into a valid default format", s) + #newzbin default format pattern = re.compile(r'(?P.*?)\s\-\s(?P.*?)\s\((?P\d+?\))', re.VERBOSE) match = pattern.match(s) @@ -251,18 +475,18 @@ def extract_song_data(s): year = match.group("year") return (name, album, year) else: - logger.info("Couldn't parse " + s + " into a valid Newbin format") + logger.info("Couldn't parse %s into a valid Newbin format", s) return (name, album, year) - + def smartMove(src, dest, delete=True): - + from headphones import logger source_dir = os.path.dirname(src) filename = os.path.basename(src) - + if os.path.isfile(os.path.join(dest, filename)): - logger.info('Destination file exists: %s' % os.path.join(dest, filename).decode(headphones.SYS_ENCODING, 'replace')) + logger.info('Destination file exists: %s', os.path.join(dest, filename).decode(headphones.SYS_ENCODING, 'replace')) title = os.path.splitext(filename)[0] ext = os.path.splitext(filename)[1] i = 1 @@ -271,12 +495,12 @@ def smartMove(src, dest, delete=True): if os.path.isfile(os.path.join(dest, newfile)): i += 1 else: - logger.info('Renaming to %s' % newfile) - try: + logger.info('Renaming to %s', newfile) + try: os.rename(src, os.path.join(source_dir, newfile)) filename = newfile except Exception, e: - logger.warn('Error renaming %s: %s' % (src.decode(headphones.SYS_ENCODING, 'replace'), str(e).decode(headphones.SYS_ENCODING, 'replace'))) + logger.warn('Error renaming %s: %s', src.decode(headphones.SYS_ENCODING, 'replace'), e) break try: @@ -286,7 +510,7 @@ def smartMove(src, dest, delete=True): shutil.copy(os.path.join(source_dir, filename), os.path.join(dest, filename)) return True except Exception, e: - logger.warn('Error moving file %s: %s' % (filename.decode(headphones.SYS_ENCODING, 'replace'), str(e).decode(headphones.SYS_ENCODING, 'replace'))) + logger.warn('Error moving file %s: %s', filename.decode(headphones.SYS_ENCODING, 'replace'), e) ######################### #Sab renaming functions # @@ -305,13 +529,13 @@ def sab_sanitize_foldername(name): """ CH_ILLEGAL = r'\/<>?*|"' CH_LEGAL = r'++{}!@#`' - + FL_ILLEGAL = CH_ILLEGAL + ':\x92"' FL_LEGAL = CH_LEGAL + "-''" - + uFL_ILLEGAL = FL_ILLEGAL.decode('latin-1') uFL_LEGAL = FL_LEGAL.decode('latin-1') - + if not name: return name if isinstance(name, unicode): @@ -340,9 +564,9 @@ def sab_sanitize_foldername(name): return name -def split_string(mystring): +def split_string(mystring, splitvar=','): mylist = [] - for each_word in mystring.split(','): + for each_word in mystring.split(splitvar): mylist.append(each_word.strip()) return mylist @@ -352,12 +576,12 @@ def create_https_certificates(ssl_cert, ssl_key): Create self-signed HTTPS certificares and store in paths 'ssl_cert' and 'ssl_key' """ from headphones import logger - + try: - from OpenSSL import crypto #@UnresolvedImport - from lib.certgen import createKeyPair, createCertRequest, createCertificate, TYPE_RSA, serial #@UnresolvedImport + from OpenSSL import crypto + from lib.certgen import createKeyPair, createCertRequest, createCertificate, TYPE_RSA, serial except: - logger.warn(u"pyopenssl module missing, please install for https access") + logger.warn("pyOpenSSL module missing, please install to enable HTTPS") return False # Create the CA Certificate @@ -374,8 +598,8 @@ def create_https_certificates(ssl_cert, ssl_key): try: open(ssl_key, 'w').write(crypto.dump_privatekey(crypto.FILETYPE_PEM, pkey)) open(ssl_cert, 'w').write(crypto.dump_certificate(crypto.FILETYPE_PEM, cert)) - except: - logger.error(u"Error creating SSL key and certificate") + except Exception, e: + logger.error("Error creating SSL key and certificate: %s", e) return False return True diff --git a/headphones/importer.py b/headphones/importer.py index a7481f76..35c956dd 100644 --- a/headphones/importer.py +++ b/headphones/importer.py @@ -17,10 +17,10 @@ from lib.pyItunes import * import time import threading import os -from lib.beets.mediafile import MediaFile +from beets.mediafile import MediaFile import headphones -from headphones import logger, helpers, db, mb, albumart, lastfm +from headphones import logger, helpers, db, mb, lastfm blacklisted_special_artist_names = ['[anonymous]','[data]','[no artist]','[traditional]','[unknown]','Various Artists'] blacklisted_special_artists = ['f731ccc4-e22a-43af-a747-64213329e088','33cf029c-63b0-41a0-9855-be2a3665fb3b',\ @@ -28,11 +28,11 @@ blacklisted_special_artists = ['f731ccc4-e22a-43af-a747-64213329e088','33cf029c- '9be7f096-97ec-4615-8957-8d40b5dcbc41','125ec42a-7229-4250-afc5-e057484327fe',\ '89ad4ac3-39f7-470e-963a-56509c546377'] - + def is_exists(artistid): myDB = db.DBConnection() - + # See if the artist is already in the database artistlist = myDB.select('SELECT ArtistID, ArtistName from artists WHERE ArtistID=?', [artistid]) @@ -46,10 +46,10 @@ def is_exists(artistid): def artistlist_to_mbids(artistlist, forced=False): for artist in artistlist: - + if not artist and not (artist == ' '): continue - + # If adding artists through Manage New Artists, they're coming through as non-unicode (utf-8?) # and screwing everything up @@ -59,50 +59,50 @@ def artistlist_to_mbids(artistlist, forced=False): except: logger.warn("Unable to convert artist to unicode so cannot do a database lookup") continue - + results = mb.findArtist(artist, limit=1) - + if not results: logger.info('No results found for: %s' % artist) continue - - try: + + try: artistid = results[0]['id'] - + except IndexError: logger.info('MusicBrainz query turned up no matches for: %s' % artist) continue - + # Check if it's blacklisted/various artists (only check if it's not forced, e.g. through library scan auto-add.) # Forced example = Adding an artist from Manage New Artists myDB = db.DBConnection() - + if not forced: bl_artist = myDB.action('SELECT * FROM blacklist WHERE ArtistID=?', [artistid]).fetchone() if bl_artist or artistid in blacklisted_special_artists: logger.info("Artist ID for '%s' is either blacklisted or Various Artists. To add artist, you must do it manually (Artist ID: %s)" % (artist, artistid)) continue - + # Add to database if it doesn't exist if not is_exists(artistid): addArtisttoDB(artistid) - + # Just update the tracks if it does else: havetracks = len(myDB.select('SELECT TrackTitle from tracks WHERE ArtistID=?', [artistid])) + len(myDB.select('SELECT TrackTitle from have WHERE ArtistName like ?', [artist])) myDB.action('UPDATE artists SET HaveTracks=? WHERE ArtistID=?', [havetracks, artistid]) - + # Delete it from the New Artists if the request came from there if forced: myDB.action('DELETE from newartists WHERE ArtistName=?', [artist]) - + # Update the similar artist tag cloud: logger.info('Updating artist information from Last.fm') try: lastfm.getSimilar() except Exception, e: logger.warn('Failed to update arist information from Last.fm: %s' % e) - + def addArtistIDListToDB(artistidlist): # Used to add a list of artist IDs to the database in a single thread logger.debug("Importer: Adding artist ids %s" % artistidlist) @@ -110,20 +110,20 @@ def addArtistIDListToDB(artistidlist): addArtisttoDB(artistid) def addArtisttoDB(artistid, extrasonly=False, forcefull=False): - + # Putting this here to get around the circular import. We're using this to update thumbnails for artist/albums from headphones import cache - + # Can't add various artists - throws an error from MB if artistid in blacklisted_special_artists: logger.warn('Cannot import blocked special purpose artist with id' + artistid) return - + # We'll use this to see if we should update the 'LastUpdated' time stamp errors = False - + myDB = db.DBConnection() - + # Delete from blacklist if it's on there myDB.action('DELETE from blacklist WHERE ArtistID=?', [artistid]) @@ -135,7 +135,7 @@ def addArtisttoDB(artistid, extrasonly=False, forcefull=False): # Don't replace a known artist name with an "Artist ID" placeholder dbartist = myDB.action('SELECT * FROM artists WHERE ArtistID=?', [artistid]).fetchone() - + # Only modify the Include Extras stuff if it's a new artist. We need it early so we know what to fetch if not dbartist: newValueDict = {"ArtistName": "Artist ID: %s" % (artistid), @@ -146,9 +146,9 @@ def addArtisttoDB(artistid, extrasonly=False, forcefull=False): newValueDict = {"Status": "Loading"} myDB.upsert("artists", newValueDict, controlValueDict) - + artist = mb.getArtist(artistid, extrasonly) - + if artist and artist.get('artist_name') in blacklisted_special_artist_names: logger.warn('Cannot import blocked special purpose artist: %s' % artist.get('artist_name')) myDB.action('DELETE from artists WHERE ArtistID=?', [artistid]) @@ -166,12 +166,12 @@ def addArtisttoDB(artistid, extrasonly=False, forcefull=False): newValueDict = {"Status": "Active"} myDB.upsert("artists", newValueDict, controlValueDict) return - + if artist['artist_name'].startswith('The '): sortname = artist['artist_name'][4:] else: sortname = artist['artist_name'] - + logger.info(u"Now adding/updating: " + artist['artist_name']) controlValueDict = {"ArtistID": artistid} @@ -179,13 +179,13 @@ def addArtisttoDB(artistid, extrasonly=False, forcefull=False): "ArtistSortName": sortname, "DateAdded": helpers.today(), "Status": "Loading"} - + myDB.upsert("artists", newValueDict, controlValueDict) # See if we need to grab extras. Artist specific extras take precedence over global option # Global options are set when adding a new artist myDB = db.DBConnection() - + try: db_artist = myDB.action('SELECT IncludeExtras, Extras from artists WHERE ArtistID=?', [artistid]).fetchone() includeExtras = db_artist['IncludeExtras'] @@ -207,16 +207,16 @@ def addArtisttoDB(artistid, extrasonly=False, forcefull=False): myDB.action("DELETE FROM allalbums WHERE AlbumID=?", [items['AlbumID']]) myDB.action("DELETE FROM tracks WHERE AlbumID=?", [items['AlbumID']]) myDB.action("DELETE FROM alltracks WHERE AlbumID=?", [items['AlbumID']]) + myDB.action('DELETE from releases WHERE ReleaseGroupID=?', [items['AlbumID']]) logger.info("[%s] Removing all references to release group %s to reflect MusicBrainz" % (artist['artist_name'], items['AlbumID'])) force_repackage = 1 else: - logger.info("[%s] Error pulling data from MusicBrainz: Maintaining dB" % artist['artist_name']) - - # Then search for releases within releasegroups, if releases don't exist, then remove from allalbums/alltracks + logger.info("[%s] There was either an error pulling data from MusicBrainz or there might not be any releases for this category" % artist['artist_name']) + # Then search for releases within releasegroups, if releases don't exist, then remove from allalbums/alltracks + album_searches = [] for rg in artist['releasegroups']: - al_title = rg['title'] today = helpers.today() rgid = rg['id'] @@ -235,7 +235,7 @@ def addArtisttoDB(artistid, extrasonly=False, forcefull=False): if check_release_date: if check_release_date[0] is None: logger.info("[%s] Now updating: %s (No Release Date)" % (artist['artist_name'], rg['title'])) - new_releases = mb.get_new_releases(rgid,includeExtras,True) + new_releases = mb.get_new_releases(rgid,includeExtras,True) else: if len(check_release_date[0]) == 10: release_date = check_release_date[0] @@ -264,7 +264,7 @@ def addArtisttoDB(artistid, extrasonly=False, forcefull=False): else: logger.info("[%s] Now adding/updating: %s (Comprehensive Force)" % (artist['artist_name'], rg['title'])) new_releases = mb.get_new_releases(rgid,includeExtras,forcefull) - + #What this does is adds new releases per artist to the allalbums + alltracks databases #new_releases = mb.get_new_releases(rgid,includeExtras) #print al_title @@ -276,7 +276,8 @@ def addArtisttoDB(artistid, extrasonly=False, forcefull=False): myDB.action("DELETE from allalbums WHERE ReleaseID=?", [rg['id']]) myDB.action("DELETE from tracks WHERE ReleaseID=?", [rg['id']]) myDB.action("DELETE from alltracks WHERE ReleaseID=?", [rg['id']]) - # This will be used later to build a hybrid release + myDB.action('DELETE from releases WHERE ReleaseGroupID=?', [rg['id']]) + # This will be used later to build a hybrid release fullreleaselist = [] #Search for releases within a release group find_hybrid_releases = myDB.action("SELECT * from allalbums WHERE AlbumID=?", [rg['id']]) @@ -305,7 +306,7 @@ def addArtisttoDB(artistid, extrasonly=False, forcefull=False): #'url': hybrid_tracks['TrackURL'], 'duration': hybrid_tracks['TrackDuration'] }) - totalTracks += 1 + totalTracks += 1 newValueDict['ReleaseID'] = hybrid_release_id newValueDict['Tracks'] = hybrid_track_array fullreleaselist.append(newValueDict) @@ -321,7 +322,7 @@ def addArtisttoDB(artistid, extrasonly=False, forcefull=False): errors = True logger.warn('[%s] Unable to get hybrid release information for %s: %s' % (artist['artist_name'],rg['title'],e)) continue - + # Use the ReleaseGroupID as the ReleaseID for the hybrid release to differentiate it # We can then use the condition WHERE ReleaseID == ReleaseGroupID to select it # The hybrid won't have a country or a format @@ -335,13 +336,13 @@ def addArtisttoDB(artistid, extrasonly=False, forcefull=False): "ReleaseDate": hybridrelease['ReleaseDate'], "Type": rg['type'] } - + myDB.upsert("allalbums", newValueDict, controlValueDict) - + for track in hybridrelease['Tracks']: cleanname = helpers.cleanName(artist['artist_name'] + ' ' + rg['title'] + ' ' + track['title']) - + controlValueDict = {"TrackID": track['id'], "ReleaseID": rg['id']} @@ -355,25 +356,25 @@ def addArtisttoDB(artistid, extrasonly=False, forcefull=False): "TrackNumber": track['number'], "CleanName": cleanname } - + match = myDB.action('SELECT Location, BitRate, Format from have WHERE CleanName=?', [cleanname]).fetchone() - + if not match: match = myDB.action('SELECT Location, BitRate, Format from have WHERE ArtistName LIKE ? AND AlbumTitle LIKE ? AND TrackTitle LIKE ?', [artist['artist_name'], rg['title'], track['title']]).fetchone() #if not match: - #match = myDB.action('SELECT Location, BitRate, Format from have WHERE TrackID=?', [track['id']]).fetchone() + #match = myDB.action('SELECT Location, BitRate, Format from have WHERE TrackID=?', [track['id']]).fetchone() if match: newValueDict['Location'] = match['Location'] newValueDict['BitRate'] = match['BitRate'] newValueDict['Format'] = match['Format'] #myDB.action('UPDATE have SET Matched="True" WHERE Location=?', [match['Location']]) myDB.action('UPDATE have SET Matched=? WHERE Location=?', (rg['id'], match['Location'])) - + myDB.upsert("alltracks", newValueDict, controlValueDict) - + # Delete matched tracks from the have table #myDB.action('DELETE from have WHERE Matched="True"') - + # If there's no release in the main albums tables, add the default (hybrid) # If there is a release, check the ReleaseID against the AlbumID to see if they differ (user updated) # check if the album already exists @@ -382,7 +383,7 @@ def addArtisttoDB(artistid, extrasonly=False, forcefull=False): releaseid = rg['id'] else: releaseid = rg_exists['ReleaseID'] - + album = myDB.action('SELECT * from allalbums WHERE ReleaseID=?', [releaseid]).fetchone() controlValueDict = {"AlbumID": rg['id']} @@ -397,13 +398,13 @@ def addArtisttoDB(artistid, extrasonly=False, forcefull=False): "ReleaseCountry": album['ReleaseCountry'], "ReleaseFormat": album['ReleaseFormat'] } - + if not rg_exists: - + today = helpers.today() - + newValueDict['DateAdded']= today - + if headphones.AUTOWANT_ALL: newValueDict['Status'] = "Wanted" elif album['ReleaseDate'] > today and headphones.AUTOWANT_UPCOMING: @@ -414,16 +415,16 @@ def addArtisttoDB(artistid, extrasonly=False, forcefull=False): newValueDict['Status'] = "Wanted" else: newValueDict['Status'] = "Skipped" - - myDB.upsert("albums", newValueDict, controlValueDict) + + myDB.upsert("albums", newValueDict, controlValueDict) tracks = myDB.action('SELECT * from alltracks WHERE ReleaseID=?', [releaseid]).fetchall() # This is used to see how many tracks you have from an album - to mark it as downloaded. Default is 80%, can be set in config as ALBUM_COMPLETION_PCT total_track_count = len(tracks) - + for track in tracks: - + controlValueDict = {"TrackID": track['TrackID'], "AlbumID": rg['id']} @@ -440,13 +441,13 @@ def addArtisttoDB(artistid, extrasonly=False, forcefull=False): "Format": track['Format'], "BitRate": track['BitRate'] } - - myDB.upsert("tracks", newValueDict, controlValueDict) + + myDB.upsert("tracks", newValueDict, controlValueDict) # Mark albums as downloaded if they have at least 80% (by default, configurable) of the album have_track_count = len(myDB.select('SELECT * from tracks WHERE AlbumID=? AND Location IS NOT NULL', [rg['id']])) marked_as_downloaded = False - + if rg_exists: if rg_exists['Status'] == 'Skipped' and ((have_track_count/float(total_track_count)) >= (headphones.ALBUM_COMPLETION_PCT/100.0)): myDB.action('UPDATE albums SET Status=? WHERE AlbumID=?', ['Downloaded', rg['id']]) @@ -458,11 +459,12 @@ def addArtisttoDB(artistid, extrasonly=False, forcefull=False): logger.info(u"[%s] Seeing if we need album art for %s" % (artist['artist_name'], rg['title'])) cache.getThumb(AlbumID=rg['id']) - - #start a search for the album if it's new, hasn't been marked as downloaded and autowant_all is selected: - if not rg_exists and not marked_as_downloaded and headphones.AUTOWANT_ALL: - from headphones import searcher - searcher.searchforalbum(albumid=rg['id']) + + # Start a search for the album if it's new, hasn't been marked as + # downloaded and autowant_all is selected. This search is deferred, + # in case the search failes and the rest of the import will halt. + if not rg_exists and not marked_as_downloaded and headphones.AUTOWANT_ALL: + album_searches.append(rg['id']) else: if skip_log == 0: logger.info(u"[%s] No new releases, so no changes made to %s" % (artist['artist_name'], rg['title'])) @@ -473,7 +475,7 @@ def addArtisttoDB(artistid, extrasonly=False, forcefull=False): havetracks = len(myDB.select('SELECT TrackTitle from tracks WHERE ArtistID=? AND Location IS NOT NULL', [artistid])) + len(myDB.select('SELECT TrackTitle from have WHERE ArtistName like ? AND Matched = "Failed"', [artist['artist_name']])) controlValueDict = {"ArtistID": artistid} - + if latestalbum: newValueDict = {"Status": "Active", "LatestAlbum": latestalbum['AlbumTitle'], @@ -485,24 +487,31 @@ def addArtisttoDB(artistid, extrasonly=False, forcefull=False): newValueDict = {"Status": "Active", "TotalTracks": totaltracks, "HaveTracks": havetracks} - + if not errors: newValueDict['LastUpdated'] = helpers.now() - + myDB.upsert("artists", newValueDict, controlValueDict) - + logger.info(u"Seeing if we need album art for: %s" % artist['artist_name']) cache.getThumb(ArtistID=artistid) - + if errors: logger.info("[%s] Finished updating artist: %s but with errors, so not marking it as updated in the database" % (artist['artist_name'], artist['artist_name'])) else: myDB.action('DELETE FROM newartists WHERE ArtistName = ?', [artist['artist_name']]) logger.info(u"Updating complete for: %s" % artist['artist_name']) - + # Start searching for newly added albums + if album_searches: + from headphones import searcher + logger.info("Start searching for %d albums.", len(album_searches)) + + for album_search in album_searches: + searcher.searchforalbum(albumid=album_search) + def addReleaseById(rid): - + myDB = db.DBConnection() rgid = None @@ -519,45 +528,45 @@ def addReleaseById(rid): try: release_dict = mb.getRelease(rid) except Exception, e: - logger.info('Unable to get release information for Release: ' + str(rid) + " " + str(e)) + logger.info('Unable to get release information for Release %s: %s', rid, e) return if not release_dict: - logger.info('Unable to get release information for Release: ' + str(rid) + " no dict") + logger.info('Unable to get release information for Release %s: no dict', rid) return - + rgid = release_dict['rgid'] artistid = release_dict['artist_id'] - + #we don't want to make more calls to MB here unless we have to, could be happening quite a lot rg_exists = myDB.select("SELECT * from albums WHERE AlbumID=?", [rgid]) - + #make sure the artist exists since I don't know what happens later if it doesn't artist_exists = myDB.select("SELECT * from artists WHERE ArtistID=?", [artistid]) - + if not artist_exists and release_dict: if release_dict['artist_name'].startswith('The '): sortname = release_dict['artist_name'][4:] else: sortname = release_dict['artist_name'] - - + + logger.info(u"Now manually adding: " + release_dict['artist_name'] + " - with status Paused") controlValueDict = {"ArtistID": release_dict['artist_id']} newValueDict = {"ArtistName": release_dict['artist_name'], "ArtistSortName": sortname, "DateAdded": helpers.today(), "Status": "Paused"} - + if headphones.INCLUDE_EXTRAS: newValueDict['IncludeExtras'] = 1 newValueDict['Extras'] = headphones.EXTRAS - + myDB.upsert("artists", newValueDict, controlValueDict) - + elif not artist_exists and not release_dict: logger.error("Artist does not exist in the database and did not get a valid response from MB. Skipping release.") return - + if not rg_exists and release_dict: #it should never be the case that we have an rg and not the artist #but if it is this will fail logger.info(u"Now adding-by-id album (" + release_dict['title'] + ") from id: " + rgid) @@ -572,16 +581,16 @@ def addReleaseById(rid): "Status": 'Wanted', "Type": release_dict['rg_type'] } - + myDB.upsert("albums", newValueDict, controlValueDict) #keep a local cache of these so that external programs that are adding releasesByID don't hammer MB myDB.action('INSERT INTO releases VALUES( ?, ?)', [rid, release_dict['rgid']]) - + for track in release_dict['tracks']: - + cleanname = helpers.cleanName(release_dict['artist_name'] + ' ' + release_dict['rg_title'] + ' ' + track['title']) - + controlValueDict = {"TrackID": track['id'], "AlbumID": rgid} newValueDict = {"ArtistID": release_dict['artist_id'], @@ -593,23 +602,23 @@ def addReleaseById(rid): "TrackNumber": track['number'], "CleanName": cleanname } - + match = myDB.action('SELECT Location, BitRate, Format from have WHERE CleanName=?', [cleanname]).fetchone() - + if not match: match = myDB.action('SELECT Location, BitRate, Format from have WHERE ArtistName LIKE ? AND AlbumTitle LIKE ? AND TrackTitle LIKE ?', [release_dict['artist_name'], release_dict['rg_title'], track['title']]).fetchone() - + #if not match: #match = myDB.action('SELECT Location, BitRate, Format from have WHERE TrackID=?', [track['id']]).fetchone() - + if match: newValueDict['Location'] = match['Location'] newValueDict['BitRate'] = match['BitRate'] newValueDict['Format'] = match['Format'] #myDB.action('DELETE from have WHERE Location=?', [match['Location']]) - + myDB.upsert("tracks", newValueDict, controlValueDict) - + #start a search for the album import searcher searcher.searchforalbum(rgid, False) @@ -655,34 +664,34 @@ def getHybridRelease(fullreleaselist): if len(fullreleaselist) == 0: raise Exception("getHybridRelease was called with an empty fullreleaselist") sortable_release_list = [] - + for release in fullreleaselist: formats = { '2xVinyl': '2', 'Vinyl': '2', 'CD': '0', - 'Cassette': '3', + 'Cassette': '3', '2xCD': '1', 'Digital Media': '0' } - + countries = { 'US': '0', 'GB': '1', 'JP': '2', } - + try: format = int(formats[release['Format']]) except: format = 3 - + try: - country = int(countries[release['Country']]) + country = int(countries[release['Country']]) except: country = 3 - + release_dict = { 'hasasin': bool(release['AlbumASIN']), 'asin': release['AlbumASIN'], @@ -695,11 +704,11 @@ def getHybridRelease(fullreleaselist): } sortable_release_list.append(release_dict) - + #necessary to make dates that miss the month and/or day show up after full dates def getSortableReleaseDate(releaseDate): if releaseDate == None: - return 'None';#change this value to change the sorting behaviour of none, returning 'None' will put it at the top + return 'None';#change this value to change the sorting behaviour of none, returning 'None' will put it at the top #which was normal behaviour for pre-ngs versions if releaseDate.count('-') == 2: return releaseDate @@ -713,12 +722,12 @@ def getHybridRelease(fullreleaselist): average_tracks = sum(x['trackscount'] for x in sortable_release_list) / float(len(sortable_release_list)) for item in sortable_release_list: item['trackscount_delta'] = abs(average_tracks - item['trackscount']) - + a = helpers.multikeysort(sortable_release_list, ['-hasasin', 'country', 'format', 'trackscount_delta']) release_dict = {'ReleaseDate' : sortable_release_list[0]['releasedate'], 'Tracks' : a[0]['tracks'], 'AlbumASIN' : a[0]['asin'] } - + return release_dict diff --git a/headphones/lastfm.py b/headphones/lastfm.py index 5e0aacaf..9f12601c 100644 --- a/headphones/lastfm.py +++ b/headphones/lastfm.py @@ -13,237 +13,141 @@ # You should have received a copy of the GNU General Public License # along with Headphones. If not, see . -import urllib, urllib2 -from xml.dom import minidom -from collections import defaultdict import random import time - import headphones -from headphones import db, logger -api_key = '395e6ec6bb557382fc41fde867bce66f' +from headphones import db, logger, request + +from collections import defaultdict + +ENTRY_POINT = 'http://ws.audioscrobbler.com/2.0/' +API_KEY = '395e6ec6bb557382fc41fde867bce66f' + +def request_lastfm(method, **kwargs): + """ + Call a Last.FM API method. Automatically sets the method and API key. Method + will return the result if no error occured. + + By default, this method will request the JSON format, since it is lighter + than XML. + """ + + # Prepare request + kwargs["method"] = method + kwargs.setdefault("api_key", API_KEY) + kwargs.setdefault("format", "json") + + # Send request + logger.debug("Calling Last.FM method: %s", method) + data = request.request_json(ENTRY_POINT, timeout=20, params=kwargs) + + # Parse response and check for errors. + if not data: + logger.error("Error calling Last.FM method: %s", method) + return + + if "error" in data: + logger.debug("Last.FM returned an error: %s", data["message"]) + return + + return data - def getSimilar(): - myDB = db.DBConnection() results = myDB.select('SELECT ArtistID from artists ORDER BY HaveTracks DESC') - + + logger.info("Fetching similar artists from Last.FM for tag cloud") artistlist = [] - + for result in results[:12]: - - url = 'http://ws.audioscrobbler.com/2.0/?method=artist.getsimilar&mbid=%s&api_key=%s' % (result['ArtistID'], api_key) - - try: - data = urllib2.urlopen(url, timeout=20).read() - except: - time.sleep(1) - continue - - if not data or len(data) < 200: - continue - - try: - d = minidom.parseString(data) - except: - logger.debug("Could not parse similar artist data from last.fm") - - node = d.documentElement - artists = d.getElementsByTagName("artist") - - for artist in artists: - namenode = artist.getElementsByTagName("name")[0].childNodes - mbidnode = artist.getElementsByTagName("mbid")[0].childNodes - - for node in namenode: - artist_name = node.data - for node in mbidnode: - artist_mbid = node.data - - try: + data = request_lastfm("artist.getsimilar", mbid=result['ArtistId']) + time.sleep(10) + + if data and "similarartists" in data: + artists = data["similarartists"]["artist"] + + for artist in artists: + try: + artist_mbid = artist["mbid"] + artist_name = artist["name"] + except TypeError: + continue + if not any(artist_mbid in x for x in results): artistlist.append((artist_name, artist_mbid)) - except: - continue - + + # Add new artists to tag cloud + logger.debug("Fetched %d artists from Last.FM", len(artistlist)) count = defaultdict(int) - + for artist, mbid in artistlist: count[artist, mbid] += 1 - - items = count.items() - - top_list = sorted(items, key=lambda x: x[1], reverse=True)[:25] - - random.shuffle(top_list) - - myDB.action('''DELETE from lastfmcloud''') - for tuple in top_list: - artist_name, artist_mbid = tuple[0] - count = tuple[1] - myDB.action('INSERT INTO lastfmcloud VALUES( ?, ?, ?)', [artist_name, artist_mbid, count]) - -def getArtists(): + items = count.items() + top_list = sorted(items, key=lambda x: x[1], reverse=True)[:25] + + random.shuffle(top_list) + + myDB.action("DELETE from lastfmcloud") + for item in top_list: + artist_name, artist_mbid = item[0] + count = item[1] + + myDB.action('INSERT INTO lastfmcloud VALUES( ?, ?, ?)', [artist_name, artist_mbid, count]) + + logger.debug("Inserted %d artists into Last.FM tag cloud", len(top_list)) + +def getArtists(): myDB = db.DBConnection() results = myDB.select('SELECT ArtistID from artists') if not headphones.LASTFM_USERNAME: + logger.warn("Last.FM username not set, not importing artists.") return - - else: - username = headphones.LASTFM_USERNAME - - url = 'http://ws.audioscrobbler.com/2.0/?method=library.getartists&limit=10000&api_key=%s&user=%s' % (api_key, username) - data = urllib2.urlopen(url, timeout=20).read() - - try: - d = minidom.parseString(data) - except: - logger.error("Could not parse artist list from last.fm data") - return - - artists = d.getElementsByTagName("artist") - - artistlist = [] - - for artist in artists: - mbidnode = artist.getElementsByTagName("mbid")[0].childNodes - for node in mbidnode: - artist_mbid = node.data - - try: + logger.info("Fetching artists from Last.FM for username: %s", headphones.LASTFM_USERNAME) + data = request_lastfm("library.getartists", limit=10000, user=headphones.LASTFM_USERNAME) + + if data and "artists" in data: + artistlist = [] + artists = data["artists"]["artist"] + logger.debug("Fetched %d artists from Last.FM", len(artists)) + + for artist in artists: + artist_mbid = artist["mbid"] + if not any(artist_mbid in x for x in results): artistlist.append(artist_mbid) - except: - continue - - from headphones import importer - - for artistid in artistlist: - importer.addArtisttoDB(artistid) - + + from headphones import importer + + for artistid in artistlist: + importer.addArtisttoDB(artistid) + + logger.info("Imported %d new artists from Last.FM", len(artistlist)) + def getTagTopArtists(tag, limit=50): myDB = db.DBConnection() results = myDB.select('SELECT ArtistID from artists') - url = 'http://ws.audioscrobbler.com/2.0/?method=tag.gettopartists&limit=%s&tag=%s&api_key=%s' % (limit, tag, api_key) - data = urllib2.urlopen(url, timeout=20).read() + logger.info("Fetching top artists from Last.FM for tag: %s", tag) + data = request_lastfm("tag.gettopartists", limit=limit, tag=tag) - try: - d = minidom.parseString(data) - except: - logger.error("Could not parse artist list from last.fm data") - return + if data and "topartists" in data: + artistlist = [] + artists = data["topartists"]["artist"] + logger.debug("Fetched %d artists from Last.FM", len(artists)) - artists = d.getElementsByTagName("artist") + for artist in artists: + artist_mbid = artist["mbid"] - artistlist = [] - - for artist in artists: - mbidnode = artist.getElementsByTagName("mbid")[0].childNodes - - for node in mbidnode: - artist_mbid = node.data - - try: if not any(artist_mbid in x for x in results): artistlist.append(artist_mbid) - except: - continue - from headphones import importer + from headphones import importer - for artistid in artistlist: - importer.addArtisttoDB(artistid) + for artistid in artistlist: + importer.addArtisttoDB(artistid) - -def getAlbumDescription(rgid, artist, album): - - myDB = db.DBConnection() - result = myDB.select('SELECT Summary from descriptions WHERE ReleaseGroupID=?', [rgid]) - - if result: - return - - params = { "method": 'album.getInfo', - "api_key": api_key, - "artist": artist.encode('utf-8'), - "album": album.encode('utf-8') - } - - searchURL = 'http://ws.audioscrobbler.com/2.0/?' + urllib.urlencode(params) - data = urllib2.urlopen(searchURL, timeout=20).read() - - if data == 'Album not found': - return - - try: - d = minidom.parseString(data) - - albuminfo = d.getElementsByTagName("album") - - for item in albuminfo: - summarynode = item.getElementsByTagName("summary")[0].childNodes - contentnode = item.getElementsByTagName("content")[0].childNodes - for node in summarynode: - summary = node.data - for node in contentnode: - content = node.data - - controlValueDict = {'ReleaseGroupID': rgid} - newValueDict = {'Summary': summary, - 'Content': content} - myDB.upsert("descriptions", newValueDict, controlValueDict) - - except: - return - -def getAlbumDescriptionOld(rgid, releaselist): - """ - This was a dumb way to do it - going to just use artist & album name but keeping this here - because I may use it to fetch and cache album art - """ - - myDB = db.DBConnection() - result = myDB.select('SELECT Summary from descriptions WHERE ReleaseGroupID=?', [rgid]) - - if result: - return - - for release in releaselist: - - mbid = release['releaseid'] - url = 'http://ws.audioscrobbler.com/2.0/?method=album.getInfo&mbid=%s&api_key=%s' % (mbid, api_key) - data = urllib.urlopen(url).read() - - if data == 'Album not found': - continue - - try: - d = minidom.parseString(data) - - albuminfo = d.getElementsByTagName("album") - - for item in albuminfo: - summarynode = item.getElementsByTagName("summary")[0].childNodes - contentnode = item.getElementsByTagName("content")[0].childNodes - for node in summarynode: - summary = node.data - for node in contentnode: - content = node.data - - controlValueDict = {'ReleaseGroupID': rgid} - newValueDict = {'ReleaseID': mbid, - 'Summary': summary, - 'Content': content} - myDB.upsert("descriptions", newValueDict, controlValueDict) - break - - except: - continue - - + logger.debug("Added %d new artists from Last.FM", len(artistlist)) \ No newline at end of file diff --git a/headphones/librarysync.py b/headphones/librarysync.py index 8706e0de..2371d918 100644 --- a/headphones/librarysync.py +++ b/headphones/librarysync.py @@ -16,10 +16,10 @@ import os import glob -from lib.beets.mediafile import MediaFile +from beets.mediafile import MediaFile import headphones -from headphones import db, logger, helpers, importer +from headphones import db, logger, helpers, importer, lastfm # You can scan a single directory and append it to the current library by specifying append=True, ArtistID & ArtistName def libraryScan(dir=None, append=False, ArtistID=None, ArtistName=None, cron=False): @@ -329,6 +329,8 @@ def libraryScan(dir=None, append=False, ArtistID=None, ArtistName=None, cron=Fal myDB.action('UPDATE artists SET HaveTracks=? WHERE ArtistID=?', [havetracks, ArtistID]) update_album_status() + if not append: + lastfm.getSimilar() logger.info('Library scan complete') #ADDED THIS SECTION TO MARK ALBUMS AS DOWNLOADED IF ARTISTS ARE ADDED EN MASSE BEFORE LIBRARY IS SCANNED diff --git a/headphones/logger.py b/headphones/logger.py index d03e13a3..c10aac58 100644 --- a/headphones/logger.py +++ b/headphones/logger.py @@ -14,83 +14,133 @@ # along with Headphones. If not, see . import os -import threading +import sys import logging +import traceback +import threading +import headphones + from logging import handlers -import headphones from headphones import helpers -MAX_SIZE = 1000000 # 1mb +# These settings are for file logging only +FILENAME = 'headphones.log' +MAX_SIZE = 1000000 # 1 MB MAX_FILES = 5 +# Headphones logger +logger = logging.getLogger('headphones') -# Simple rotating log handler that uses RotatingFileHandler -class RotatingLogger(object): +class LogListHandler(logging.Handler): + """ + Log handler for Web UI. + """ - def __init__(self, filename, max_size, max_files): - - self.filename = filename - self.max_size = max_size - self.max_files = max_files - - - def initLogger(self, verbose=1): - - l = logging.getLogger('headphones') - l.setLevel(logging.DEBUG) - - self.filename = os.path.join(headphones.LOG_DIR, self.filename) - - filehandler = handlers.RotatingFileHandler(self.filename, maxBytes=self.max_size, backupCount=self.max_files) - filehandler.setLevel(logging.DEBUG) - - fileformatter = logging.Formatter('%(asctime)s - %(levelname)-7s :: %(message)s', '%d-%b-%Y %H:%M:%S') - - filehandler.setFormatter(fileformatter) - l.addHandler(filehandler) - - if verbose: - consolehandler = logging.StreamHandler() - if verbose == 1: - consolehandler.setLevel(logging.INFO) - if verbose == 2: - consolehandler.setLevel(logging.DEBUG) - consoleformatter = logging.Formatter('%(asctime)s - %(levelname)s :: %(message)s', '%d-%b-%Y %H:%M:%S') - consolehandler.setFormatter(consoleformatter) - l.addHandler(consolehandler) - - def log(self, message, level): + def emit(self, record): + message = self.format(record) + message = message.replace("\n", "
    ") - logger = logging.getLogger('headphones') - - threadname = threading.currentThread().getName() - - if level != 'DEBUG': - headphones.LOG_LIST.insert(0, (helpers.now(), message, level, threadname)) - - message = threadname + ' : ' + message + headphones.LOG_LIST.insert(0, (helpers.now(), message, record.levelname, record.threadName)) - if level == 'DEBUG': - logger.debug(message) - elif level == 'INFO': - logger.info(message) - elif level == 'WARNING': - logger.warn(message) - else: - logger.error(message) +def initLogger(verbose=1): + """ + Setup logging for Headphones. It uses the logger instance with the name + 'headphones'. Three log handlers are added: -headphones_log = RotatingLogger('headphones.log', MAX_SIZE, MAX_FILES) + * RotatingFileHandler: for the file headphones.log + * LogListHandler: for Web UI + * StreamHandler: for console (if verbose > 0) + """ -def debug(message): - headphones_log.log(message, level='DEBUG') + # Configure the logger to accept all messages + logger.propagate = False + logger.setLevel(logging.DEBUG) -def info(message): - headphones_log.log(message, level='INFO') - -def warn(message): - headphones_log.log(message, level='WARNING') - -def error(message): - headphones_log.log(message, level='ERROR') - + # Setup file logger + filename = os.path.join(headphones.LOG_DIR, FILENAME) + + file_formatter = logging.Formatter('%(asctime)s - %(levelname)-7s :: %(threadName)s : %(message)s', '%d-%b-%Y %H:%M:%S') + file_handler = handlers.RotatingFileHandler(filename, maxBytes=MAX_SIZE, backupCount=MAX_FILES) + file_handler.setLevel(logging.DEBUG) + file_handler.setFormatter(file_formatter) + + logger.addHandler(file_handler) + + # Add list logger + loglist_handler = LogListHandler() + loglist_handler.setLevel(logging.INFO) + + logger.addHandler(loglist_handler) + + # Setup console logger + if verbose: + console_formatter = logging.Formatter('%(asctime)s - %(levelname)s :: %(threadName)s : %(message)s', '%d-%b-%Y %H:%M:%S') + console_handler = logging.StreamHandler() + console_handler.setFormatter(console_formatter) + + if verbose == 1: + console_handler.setLevel(logging.INFO) + elif verbose == 2: + console_handler.setLevel(logging.DEBUG) + + logger.addHandler(console_handler) + + # Install exception hooks + initHooks() + +def initHooks(global_exceptions=True, thread_exceptions=True, pass_original=True): + """ + This method installs exception catching mechanisms. Any exception caught + will pass through the exception hook, and will be logged to the logger as + an error. Additionally, a traceback is provided. + + This is very useful for crashing threads and any other bugs, that may not + be exposed when running as daemon. + + The default exception hook is still considered, if pass_original is True. + """ + + def excepthook(*exception_info): + # We should always catch this to prevent loops! + try: + message = "".join(traceback.format_exception(*exception_info)) + logger.error("Uncaught exception: %s", message) + except: + pass + + # Original excepthook + if pass_original: + sys.__excepthook__(*exception_info) + + # Global exception hook + if global_exceptions: + sys.excepthook = excepthook + + # Thread exception hook + if thread_exceptions: + old_init = threading.Thread.__init__ + + def new_init(self, *args, **kwargs): + old_init(self, *args, **kwargs) + old_run = self.run + + def new_run(*args, **kwargs): + try: + old_run(*args, **kwargs) + except (KeyboardInterrupt, SystemExit): + raise + except: + excepthook(*sys.exc_info()) + self.run = new_run + + # Monkey patch the run() by monkey patching the __init__ method + threading.Thread.__init__ = new_init + +# Expose logger methods +info = logger.info +warn = logger.warn +error = logger.error +debug = logger.debug +warning = logger.warning +exception = logger.exception \ No newline at end of file diff --git a/headphones/lyrics.py b/headphones/lyrics.py index 6899359a..aa3c952a 100644 --- a/headphones/lyrics.py +++ b/headphones/lyrics.py @@ -14,11 +14,9 @@ # along with Headphones. If not, see . import re -import urllib, urllib2 -from xml.dom import minidom import htmlentitydefs -from headphones import logger +from headphones import logger, request def getLyrics(artist, song): @@ -26,22 +24,14 @@ def getLyrics(artist, song): "song": song.encode('utf-8'), "fmt": 'xml' } - - searchURL = 'http://lyrics.wikia.com/api.php?' + urllib.urlencode(params) + + url = 'http://lyrics.wikia.com/api.php' + data = request.request_minidom(url, params=params) - try: - data = urllib2.urlopen(searchURL, timeout=20).read() - except Exception, e: - logger.warn('Error opening: %s. Error: %s' % (searchURL, e)) + if not data: return - try: - parseddata = minidom.parseString(data) - except Exception, e: - logger.warn('Error parsing data from url: %s. Error: %s' % (searchURL, e)) - return - - url = parseddata.getElementsByTagName("url") + url = data.getElementsByTagName("url") if url: lyricsurl = url[0].firstChild.nodeValue @@ -49,12 +39,12 @@ def getLyrics(artist, song): logger.info('No lyrics found for %s - %s' % (artist, song)) return - try: - lyricspage = urllib.urlopen(lyricsurl).read() - except Exception, e: - logger.warn('Error fetching lyrics from: %s. Error: %s' % (lyricsurl, e)) + lyricspage = request.request_content(lyricsurl) + + if not lyricspage: + logger.warn('Error fetching lyrics from: %s' % lyricsurl) return - + m = re.compile('''
    .*?
    (.*?)" % (" "*indent, token["data"])) - elif type == "Doctype": - if token["name"]: - if token["publicId"]: - output.append("""%s"""% - (" "*indent, token["name"], - token["publicId"], - token["systemId"] and token["systemId"] or "")) - elif token["systemId"]: - output.append("""%s"""% - (" "*indent, token["name"], - token["systemId"])) - else: - output.append("%s"%(" "*indent, - token["name"])) - else: - output.append("%s" % (" "*indent,)) - elif type in ("Characters", "SpaceCharacters"): - output.append("%s\"%s\"" % (" "*indent, token["data"])) - else: - pass # TODO: what to do with errors? - return u"\n".join(output) - -import re -attrlist = re.compile(r"^(\s+)\w+=.*(\n\1\w+=.*)+",re.M) -def sortattrs(x): - lines = x.group(0).split("\n") - lines.sort() - return "\n".join(lines) - - -class TokenTestCase(unittest.TestCase): - def test_all_tokens(self): - expected = [ - {'data': {}, 'type': 'StartTag', 'namespace': u'http://www.w3.org/1999/xhtml', 'name': u'html'}, - {'data': {}, 'type': 'StartTag', 'namespace': u'http://www.w3.org/1999/xhtml', 'name': u'head'}, - {'data': {}, 'type': 'EndTag', 'namespace': u'http://www.w3.org/1999/xhtml', 'name': u'head'}, - {'data': {}, 'type': 'StartTag', 'namespace': u'http://www.w3.org/1999/xhtml', 'name': u'body'}, - {'data': u'a', 'type': 'Characters'}, - {'data': {}, 'type': 'StartTag', 'namespace': u'http://www.w3.org/1999/xhtml', 'name': u'div'}, - {'data': u'b', 'type': 'Characters'}, - {'data': {}, 'type': 'EndTag', 'namespace': u'http://www.w3.org/1999/xhtml', 'name': u'div'}, - {'data': u'c', 'type': 'Characters'}, - {'data': {}, 'type': 'EndTag', 'namespace': u'http://www.w3.org/1999/xhtml', 'name': u'body'}, - {'data': {}, 'type': 'EndTag', 'namespace': u'http://www.w3.org/1999/xhtml', 'name': u'html'} - ] - for treeName, treeCls in treeTypes.iteritems(): - p = html5parser.HTMLParser(tree = treeCls["builder"]) - document = p.parse("a
    b
    c") - document = treeCls.get("adapter", lambda x: x)(document) - output = treeCls["walker"](document) - for expectedToken, outputToken in zip(expected, output): - self.assertEquals(expectedToken, outputToken) - -def run_test(innerHTML, input, expected, errors, treeClass): - try: - p = html5parser.HTMLParser(tree = treeClass["builder"]) - if innerHTML: - document = p.parseFragment(StringIO.StringIO(input), innerHTML) - else: - document = p.parse(StringIO.StringIO(input)) - except constants.DataLossWarning: - #Ignore testcases we know we don't pass - return - - document = treeClass.get("adapter", lambda x: x)(document) - try: - output = convertTokens(treeClass["walker"](document)) - output = attrlist.sub(sortattrs, output) - expected = attrlist.sub(sortattrs, convertExpected(expected)) - assert expected == output, "\n".join([ - "", "Input:", input, - "", "Expected:", expected, - "", "Received:", output - ]) - except NotImplementedError: - pass # Amnesty for those that confess... - -def test_treewalker(): - sys.stdout.write('Testing tree walkers '+ " ".join(treeTypes.keys()) + "\n") - - for treeName, treeCls in treeTypes.iteritems(): - files = html5lib_test_files('tree-construction') - for filename in files: - testName = os.path.basename(filename).replace(".dat","") - - tests = TestData(filename, "data") - - for index, test in enumerate(tests): - (input, errors, - innerHTML, expected) = [test[key] for key in ("data", "errors", - "document-fragment", - "document")] - errors = errors.split("\n") - yield run_test, innerHTML, input, expected, errors, treeCls - - diff --git a/html5lib/tests/test_whitespace_filter.py b/html5lib/tests/test_whitespace_filter.py deleted file mode 100644 index 96a39dbc..00000000 --- a/html5lib/tests/test_whitespace_filter.py +++ /dev/null @@ -1,123 +0,0 @@ -import unittest - -from html5lib.filters.whitespace import Filter -from html5lib.constants import spaceCharacters -spaceCharacters = u"".join(spaceCharacters) - -class TestCase(unittest.TestCase): - def runTest(self, input, expected): - output = list(Filter(input)) - errorMsg = "\n".join(["\n\nInput:", str(input), - "\nExpected:", str(expected), - "\nReceived:", str(output)]) - self.assertEquals(output, expected, errorMsg) - - def runTestUnmodifiedOutput(self, input): - self.runTest(input, input) - - def testPhrasingElements(self): - self.runTestUnmodifiedOutput( - [{"type": u"Characters", "data": u"This is a " }, - {"type": u"StartTag", "name": u"span", "data": [] }, - {"type": u"Characters", "data": u"phrase" }, - {"type": u"EndTag", "name": u"span", "data": []}, - {"type": u"SpaceCharacters", "data": u" " }, - {"type": u"Characters", "data": u"with" }, - {"type": u"SpaceCharacters", "data": u" " }, - {"type": u"StartTag", "name": u"em", "data": [] }, - {"type": u"Characters", "data": u"emphasised text" }, - {"type": u"EndTag", "name": u"em", "data": []}, - {"type": u"Characters", "data": u" and an " }, - {"type": u"StartTag", "name": u"img", "data": [[u"alt", u"image"]] }, - {"type": u"Characters", "data": u"." }]) - - def testLeadingWhitespace(self): - self.runTest( - [{"type": u"StartTag", "name": u"p", "data": []}, - {"type": u"SpaceCharacters", "data": spaceCharacters}, - {"type": u"Characters", "data": u"foo"}, - {"type": u"EndTag", "name": u"p", "data": []}], - [{"type": u"StartTag", "name": u"p", "data": []}, - {"type": u"SpaceCharacters", "data": u" "}, - {"type": u"Characters", "data": u"foo"}, - {"type": u"EndTag", "name": u"p", "data": []}]) - - def testLeadingWhitespaceAsCharacters(self): - self.runTest( - [{"type": u"StartTag", "name": u"p", "data": []}, - {"type": u"Characters", "data": spaceCharacters + u"foo"}, - {"type": u"EndTag", "name": u"p", "data": []}], - [{"type": u"StartTag", "name": u"p", "data": []}, - {"type": u"Characters", "data": u" foo"}, - {"type": u"EndTag", "name": u"p", "data": []}]) - - def testTrailingWhitespace(self): - self.runTest( - [{"type": u"StartTag", "name": u"p", "data": []}, - {"type": u"Characters", "data": u"foo"}, - {"type": u"SpaceCharacters", "data": spaceCharacters}, - {"type": u"EndTag", "name": u"p", "data": []}], - [{"type": u"StartTag", "name": u"p", "data": []}, - {"type": u"Characters", "data": u"foo"}, - {"type": u"SpaceCharacters", "data": u" "}, - {"type": u"EndTag", "name": u"p", "data": []}]) - - def testTrailingWhitespaceAsCharacters(self): - self.runTest( - [{"type": u"StartTag", "name": u"p", "data": []}, - {"type": u"Characters", "data": u"foo" + spaceCharacters}, - {"type": u"EndTag", "name": u"p", "data": []}], - [{"type": u"StartTag", "name": u"p", "data": []}, - {"type": u"Characters", "data": u"foo "}, - {"type": u"EndTag", "name": u"p", "data": []}]) - - def testWhitespace(self): - self.runTest( - [{"type": u"StartTag", "name": u"p", "data": []}, - {"type": u"Characters", "data": u"foo" + spaceCharacters + "bar"}, - {"type": u"EndTag", "name": u"p", "data": []}], - [{"type": u"StartTag", "name": u"p", "data": []}, - {"type": u"Characters", "data": u"foo bar"}, - {"type": u"EndTag", "name": u"p", "data": []}]) - - def testLeadingWhitespaceInPre(self): - self.runTestUnmodifiedOutput( - [{"type": u"StartTag", "name": u"pre", "data": []}, - {"type": u"SpaceCharacters", "data": spaceCharacters}, - {"type": u"Characters", "data": u"foo"}, - {"type": u"EndTag", "name": u"pre", "data": []}]) - - def testLeadingWhitespaceAsCharactersInPre(self): - self.runTestUnmodifiedOutput( - [{"type": u"StartTag", "name": u"pre", "data": []}, - {"type": u"Characters", "data": spaceCharacters + u"foo"}, - {"type": u"EndTag", "name": u"pre", "data": []}]) - - def testTrailingWhitespaceInPre(self): - self.runTestUnmodifiedOutput( - [{"type": u"StartTag", "name": u"pre", "data": []}, - {"type": u"Characters", "data": u"foo"}, - {"type": u"SpaceCharacters", "data": spaceCharacters}, - {"type": u"EndTag", "name": u"pre", "data": []}]) - - def testTrailingWhitespaceAsCharactersInPre(self): - self.runTestUnmodifiedOutput( - [{"type": u"StartTag", "name": u"pre", "data": []}, - {"type": u"Characters", "data": u"foo" + spaceCharacters}, - {"type": u"EndTag", "name": u"pre", "data": []}]) - - def testWhitespaceInPre(self): - self.runTestUnmodifiedOutput( - [{"type": u"StartTag", "name": u"pre", "data": []}, - {"type": u"Characters", "data": u"foo" + spaceCharacters + "bar"}, - {"type": u"EndTag", "name": u"pre", "data": []}]) - -def buildTestSuite(): - return unittest.defaultTestLoader.loadTestsFromName(__name__) - -def main(): - buildTestSuite() - unittest.main() - -if __name__ == "__main__": - main() diff --git a/html5lib/tests/testdata/encoding/test-yahoo-jp.dat b/html5lib/tests/testdata/encoding/test-yahoo-jp.dat deleted file mode 100644 index 3c25ecb2..00000000 --- a/html5lib/tests/testdata/encoding/test-yahoo-jp.dat +++ /dev/null @@ -1,10 +0,0 @@ -#data - - - - -Yahoo! JAPAN - - -#encoding -iso8859-2 - -#data - -

    -#encoding -iso8859-2 - -#data - - - -#encoding -iso8859-2 diff --git a/html5lib/tests/testdata/encoding/tests2.dat b/html5lib/tests/testdata/encoding/tests2.dat deleted file mode 100644 index eee44984..00000000 --- a/html5lib/tests/testdata/encoding/tests2.dat +++ /dev/null @@ -1,115 +0,0 @@ -#data - -#encoding -utf-8 - -#data - - -#encoding -windows-1252 - -#data - -#encoding -utf-8 - -#data - -#encoding -windows-1252 - -#data - -#encoding -utf-8 - -#data - -#encoding -utf-8 - -#data - -#encoding -utf-8 - -#data - -#encoding -utf-8 - -#data - - -#encoding -utf-8 - -#data - - -#encoding -utf-8 - -#data -ñ - -#encoding -utf-8 diff --git a/html5lib/tests/testdata/sanitizer/tests1.dat b/html5lib/tests/testdata/sanitizer/tests1.dat deleted file mode 100644 index c741cb8c..00000000 --- a/html5lib/tests/testdata/sanitizer/tests1.dat +++ /dev/null @@ -1,501 +0,0 @@ -[ - { - "name": "IE_Comments", - "input": "", - "output": "" - }, - - { - "name": "IE_Comments_2", - "input": "", - "output": "<script>alert('XSS');</script>", - "rexml": "Ill-formed XHTML!" - }, - - { - "name": "allow_colons_in_path_component", - "input": "foo", - "output": "foo" - }, - - { - "name": "background_attribute", - "input": "
    ", - "output": "
    ", - "xhtml": "
    ", - "rexml": "
    " - }, - - { - "name": "bgsound", - "input": "", - "output": "<bgsound src=\"javascript:alert('XSS');\"/>", - "rexml": "<bgsound src=\"javascript:alert('XSS');\"></bgsound>" - }, - - { - "name": "div_background_image_unicode_encoded", - "input": "
    foo
    ", - "output": "
    foo
    " - }, - - { - "name": "div_expression", - "input": "
    foo
    ", - "output": "
    foo
    " - }, - - { - "name": "double_open_angle_brackets", - "input": "", - "rexml": "Ill-formed XHTML!" - }, - - { - "name": "double_open_angle_brackets_2", - "input": "", - "output": "<script XSS=\"\" src=\"http://ha.ckers.org/xss.js\"></script>", - "rexml": "Ill-formed XHTML!" - }, - - { - "name": "non_alpha_non_digit_2", - "input": "foo", - "output": "foo", - "rexml": "Ill-formed XHTML!" - }, - - { - "name": "non_alpha_non_digit_3", - "input": "", - "output": "", - "rexml": "Ill-formed XHTML!" - }, - - { - "name": "non_alpha_non_digit_II", - "input": "foo", - "output": "foo", - "rexml": "Ill-formed XHTML!" - }, - - { - "name": "non_alpha_non_digit_III", - "input": "foo", - "output": "foo", - "rexml": "Ill-formed XHTML!" - }, - - { - "name": "platypus", - "input": "never trust your upstream platypus", - "output": "never trust your upstream platypus" - }, - - { - "name": "protocol_resolution_in_script_tag", - "input": "", - "output": "<script src=\"//ha.ckers.org/.j\"></script>", - "rexml": "Ill-formed XHTML!" - }, - - { - "name": "should_allow_anchors", - "input": "", - "output": "<script>baz</script>" - }, - - { - "name": "should_allow_image_alt_attribute", - "input": "foo", - "output": "foo", - "rexml": "foo" - }, - - { - "name": "should_allow_image_height_attribute", - "input": "", - "output": "", - "rexml": "" - }, - - { - "name": "should_allow_image_src_attribute", - "input": "", - "output": "", - "rexml": "" - }, - - { - "name": "should_allow_image_width_attribute", - "input": "", - "output": "", - "rexml": "" - }, - - { - "name": "should_handle_blank_text", - "input": "", - "output": "" - }, - - { - "name": "should_handle_malformed_image_tags", - "input": "\">", - "output": "<script>alert(\"XSS\")</script>\">", - "rexml": "Ill-formed XHTML!" - }, - - { - "name": "should_handle_non_html", - "input": "abc", - "output": "abc" - }, - - { - "name": "should_not_fall_for_ridiculous_hack", - "input": "", - "output": "", - "rexml": "" - }, - - { - "name": "should_not_fall_for_xss_image_hack_0", - "input": "", - "output": "", - "rexml": "" - }, - - { - "name": "should_not_fall_for_xss_image_hack_1", - "input": "", - "output": "", - "rexml": "Ill-formed XHTML!" - }, - - { - "name": "should_not_fall_for_xss_image_hack_10", - "input": "", - "output": "", - "rexml": "" - }, - - { - "name": "should_not_fall_for_xss_image_hack_11", - "input": "", - "output": "", - "rexml": "" - }, - - { - "name": "should_not_fall_for_xss_image_hack_12", - "input": "", - "output": "", - "rexml": "" - }, - - { - "name": "should_not_fall_for_xss_image_hack_13", - "input": "", - "output": "", - "rexml": "" - }, - - { - "name": "should_not_fall_for_xss_image_hack_14", - "input": "", - "output": "", - "rexml": "" - }, - - { - "name": "should_not_fall_for_xss_image_hack_2", - "input": "", - "output": "", - "rexml": "" - }, - - { - "name": "should_not_fall_for_xss_image_hack_3", - "input": "", - "output": "", - "rexml": "" - }, - - { - "name": "should_not_fall_for_xss_image_hack_4", - "input": "", - "output": "", - "rexml": "" - }, - - { - "name": "should_not_fall_for_xss_image_hack_5", - "input": "", - "output": "", - "rexml": "" - }, - - { - "name": "should_not_fall_for_xss_image_hack_6", - "input": "", - "output": "", - "rexml": "" - }, - - { - "name": "should_not_fall_for_xss_image_hack_7", - "input": "", - "output": "", - "rexml": "" - }, - - { - "name": "should_not_fall_for_xss_image_hack_8", - "input": "", - "output": "", - "rexml": "" - }, - - { - "name": "should_not_fall_for_xss_image_hack_9", - "input": "", - "output": "", - "rexml": "" - }, - - { - "name": "should_sanitize_half_open_scripts", - "input": "", - "rexml": "Ill-formed XHTML!" - }, - - { - "name": "should_sanitize_invalid_script_tag", - "input": "", - "output": "<script XSS=\"\" SRC=\"http://ha.ckers.org/xss.js\"></script>", - "rexml": "Ill-formed XHTML!" - }, - - { - "name": "should_sanitize_script_tag_with_multiple_open_brackets", - "input": "<", - "output": "<<script>alert(\"XSS\");//<</script>", - "rexml": "Ill-formed XHTML!" - }, - - { - "name": "should_sanitize_script_tag_with_multiple_open_brackets_2", - "input": " -#errors -Line: 1 Col: 9 Unexpected end tag (strong). Expected DOCTYPE. -Line: 1 Col: 9 Unexpected end tag (strong) after the (implied) root element. -Line: 1 Col: 13 Unexpected end tag (b) after the (implied) root element. -Line: 1 Col: 18 Unexpected end tag (em) after the (implied) root element. -Line: 1 Col: 22 Unexpected end tag (i) after the (implied) root element. -Line: 1 Col: 26 Unexpected end tag (u) after the (implied) root element. -Line: 1 Col: 35 Unexpected end tag (strike) after the (implied) root element. -Line: 1 Col: 39 Unexpected end tag (s) after the (implied) root element. -Line: 1 Col: 47 Unexpected end tag (blink) after the (implied) root element. -Line: 1 Col: 52 Unexpected end tag (tt) after the (implied) root element. -Line: 1 Col: 58 Unexpected end tag (pre) after the (implied) root element. -Line: 1 Col: 64 Unexpected end tag (big) after the (implied) root element. -Line: 1 Col: 72 Unexpected end tag (small) after the (implied) root element. -Line: 1 Col: 79 Unexpected end tag (font) after the (implied) root element. -Line: 1 Col: 88 Unexpected end tag (select) after the (implied) root element. -Line: 1 Col: 93 Unexpected end tag (h1) after the (implied) root element. -Line: 1 Col: 98 Unexpected end tag (h2) after the (implied) root element. -Line: 1 Col: 103 Unexpected end tag (h3) after the (implied) root element. -Line: 1 Col: 108 Unexpected end tag (h4) after the (implied) root element. -Line: 1 Col: 113 Unexpected end tag (h5) after the (implied) root element. -Line: 1 Col: 118 Unexpected end tag (h6) after the (implied) root element. -Line: 1 Col: 125 Unexpected end tag (body) after the (implied) root element. -Line: 1 Col: 130 Unexpected end tag (br). Treated as br element. -Line: 1 Col: 134 End tag (a) violates step 1, paragraph 1 of the adoption agency algorithm. -Line: 1 Col: 140 This element (img) has no end tag. -Line: 1 Col: 148 Unexpected end tag (title). Ignored. -Line: 1 Col: 155 Unexpected end tag (span). Ignored. -Line: 1 Col: 163 Unexpected end tag (style). Ignored. -Line: 1 Col: 172 Unexpected end tag (script). Ignored. -Line: 1 Col: 180 Unexpected end tag (table). Ignored. -Line: 1 Col: 185 Unexpected end tag (th). Ignored. -Line: 1 Col: 190 Unexpected end tag (td). Ignored. -Line: 1 Col: 195 Unexpected end tag (tr). Ignored. -Line: 1 Col: 203 This element (frame) has no end tag. -Line: 1 Col: 210 This element (area) has no end tag. -Line: 1 Col: 217 Unexpected end tag (link). Ignored. -Line: 1 Col: 225 This element (param) has no end tag. -Line: 1 Col: 230 This element (hr) has no end tag. -Line: 1 Col: 238 This element (input) has no end tag. -Line: 1 Col: 244 Unexpected end tag (col). Ignored. -Line: 1 Col: 251 Unexpected end tag (base). Ignored. -Line: 1 Col: 258 Unexpected end tag (meta). Ignored. -Line: 1 Col: 269 This element (basefont) has no end tag. -Line: 1 Col: 279 This element (bgsound) has no end tag. -Line: 1 Col: 287 This element (embed) has no end tag. -Line: 1 Col: 296 This element (spacer) has no end tag. -Line: 1 Col: 300 Unexpected end tag (p). Ignored. -Line: 1 Col: 305 End tag (dd) seen too early. Expected other end tag. -Line: 1 Col: 310 End tag (dt) seen too early. Expected other end tag. -Line: 1 Col: 320 Unexpected end tag (caption). Ignored. -Line: 1 Col: 331 Unexpected end tag (colgroup). Ignored. -Line: 1 Col: 339 Unexpected end tag (tbody). Ignored. -Line: 1 Col: 347 Unexpected end tag (tfoot). Ignored. -Line: 1 Col: 355 Unexpected end tag (thead). Ignored. -Line: 1 Col: 365 End tag (address) seen too early. Expected other end tag. -Line: 1 Col: 378 End tag (blockquote) seen too early. Expected other end tag. -Line: 1 Col: 387 End tag (center) seen too early. Expected other end tag. -Line: 1 Col: 393 Unexpected end tag (dir). Ignored. -Line: 1 Col: 399 End tag (div) seen too early. Expected other end tag. -Line: 1 Col: 404 End tag (dl) seen too early. Expected other end tag. -Line: 1 Col: 415 End tag (fieldset) seen too early. Expected other end tag. -Line: 1 Col: 425 End tag (listing) seen too early. Expected other end tag. -Line: 1 Col: 432 End tag (menu) seen too early. Expected other end tag. -Line: 1 Col: 437 End tag (ol) seen too early. Expected other end tag. -Line: 1 Col: 442 End tag (ul) seen too early. Expected other end tag. -Line: 1 Col: 447 End tag (li) seen too early. Expected other end tag. -Line: 1 Col: 454 End tag (nobr) violates step 1, paragraph 1 of the adoption agency algorithm. -Line: 1 Col: 460 This element (wbr) has no end tag. -Line: 1 Col: 476 End tag (button) seen too early. Expected other end tag. -Line: 1 Col: 486 End tag (marquee) seen too early. Expected other end tag. -Line: 1 Col: 495 End tag (object) seen too early. Expected other end tag. -Line: 1 Col: 513 Unexpected end tag (html). Ignored. -Line: 1 Col: 513 Unexpected end tag (frameset). Ignored. -Line: 1 Col: 520 Unexpected end tag (head). Ignored. -Line: 1 Col: 529 Unexpected end tag (iframe). Ignored. -Line: 1 Col: 537 This element (image) has no end tag. -Line: 1 Col: 547 This element (isindex) has no end tag. -Line: 1 Col: 557 Unexpected end tag (noembed). Ignored. -Line: 1 Col: 568 Unexpected end tag (noframes). Ignored. -Line: 1 Col: 579 Unexpected end tag (noscript). Ignored. -Line: 1 Col: 590 Unexpected end tag (optgroup). Ignored. -Line: 1 Col: 599 Unexpected end tag (option). Ignored. -Line: 1 Col: 611 Unexpected end tag (plaintext). Ignored. -Line: 1 Col: 622 Unexpected end tag (textarea). Ignored. -#document -| -| -| -|
    -|

    - -#data -

    +
    +

    Growl

    +
    + +
    +
    +
    + +
    +
    + +
    +
    + +
    +
    +

    Prowl

    @@ -644,6 +677,19 @@
    +
    +

    LMS

    +
    + +
    +
    +
    + + + e.g. http://localhost:9000. Seperate hosts with commas +
    +
    +
    @@ -685,6 +731,9 @@ +
    + +
    @@ -792,6 +841,18 @@ +
    +
    + +
    +
    +
    +
    + + + Set equal to the number of cores, or 0 for auto +
    +
    Audio Properties @@ -1177,6 +1238,46 @@ } }); + if ($("#move_files").is(":checked")) + { + $("#move_files_options").show(); + } + else + { + $("#move_files_options").hide(); + } + + $("#move_files").click(function(){ + if ($("#move_files").is(":checked")) + { + $("#move_files_options").slideDown(); + } + else + { + $("#move_files_options").slideUp(); + } + }); + + if ($("#growl").is(":checked")) + { + $("#growloptions").show(); + } + else + { + $("#growloptions").hide(); + } + + $("#growl").click(function(){ + if ($("#growl").is(":checked")) + { + $("#growloptions").slideDown(); + } + else + { + $("#growloptions").slideUp(); + } + }); + if ($("#prowl").is(":checked")) { $("#prowloptions").show(); @@ -1217,6 +1318,26 @@ } }); + if ($("#lms").is(":checked")) + { + $("#lmsoptions").show(); + } + else + { + $("#lmsoptions").hide(); + } + + $("#lms").click(function(){ + if ($("#lms").is(":checked")) + { + $("#lmsoptions").slideDown(); + } + else + { + $("#lmsoptions").slideUp(); + } + }); + if ($("#plex").is(":checked")) { $("#plexoptions").show(); diff --git a/data/interfaces/default/css/style.css b/data/interfaces/default/css/style.css index 82c3d066..4e7b0739 100644 --- a/data/interfaces/default/css/style.css +++ b/data/interfaces/default/css/style.css @@ -393,6 +393,9 @@ form .checkbox small { margin: 0 !important; width: auto; } +form .indent input { + margin-left: 15px; +} ul, ol { margin-left: 2em; @@ -688,7 +691,7 @@ footer { } #subhead #subhead_container #subhead_menu { float: right; - margin-top: 5px; + margin-top: 0px; position: relative; z-index: 99; } @@ -713,6 +716,11 @@ footer { color: #FFF; border-color: #518CC6 #518CC6 #2A65A0; } +#subhead #subhead_container #back_to_previous_link { + margin-top: 20px; + position: relative; + z-index: 99; +} div#searchbar { border-left: 1px solid #FAFAFA; -moz-box-shadow: -1px 0 0 #e0e0e0; @@ -769,7 +777,7 @@ div#searchbar .mini-icon { _height: 302px; background-color: #FFF; clear: both; - margin: 30px auto 0; + margin: 60px auto 0; min-height: 100px; position: relative; width: 100%; @@ -1051,6 +1059,22 @@ div#artistheader h2 a { text-align: center; vertical-align: middle; } +#downloads_table th#title { + text-align: center; + min-width: 500px; +} +#downloads_table th#size { + text-align: center; + min-width: 80px; +} +#downloads_table th#provider { + text-align: center; + min-width: 100px; +} +#downloads_table th#kind { + text-align: center; + min-width: 80px; +} #history_table { background-color: #FFF; font-size: 13px; @@ -1212,6 +1236,7 @@ div#artistheader h2 a { position: relative; top: 7px; } +#trashcan {margin-top:15px;} .cloudtag { font-size: 16px; } @@ -1321,6 +1346,15 @@ div#artistheader h2 a { .clearfix:after { clear: both; } +.override-float { + float: none !important; + margin-bottom: 0px !important; + clear: none !important; + display: inline !important; + font-size: 10px; + line-height: 10px; + height: 10px; +} #album_table th#albumname, #upcoming_table th#artistname, #wanted_table th#artistname { diff --git a/data/interfaces/default/history.html b/data/interfaces/default/history.html index baf72b3d..b7cef364 100644 --- a/data/interfaces/default/history.html +++ b/data/interfaces/default/history.html @@ -26,6 +26,7 @@
    Size Status
    ${helpers.bytes_to_mb(item['Size'])} ${item['Status']} [retry][new]

    -#errors -Line: 1 Col: 7 Unexpected start tag (table). Expected DOCTYPE. -Line: 1 Col: 20 Unexpected end tag (strong) in table context caused voodoo mode. -Line: 1 Col: 20 End tag (strong) violates step 1, paragraph 1 of the adoption agency algorithm. -Line: 1 Col: 24 Unexpected end tag (b) in table context caused voodoo mode. -Line: 1 Col: 24 End tag (b) violates step 1, paragraph 1 of the adoption agency algorithm. -Line: 1 Col: 29 Unexpected end tag (em) in table context caused voodoo mode. -Line: 1 Col: 29 End tag (em) violates step 1, paragraph 1 of the adoption agency algorithm. -Line: 1 Col: 33 Unexpected end tag (i) in table context caused voodoo mode. -Line: 1 Col: 33 End tag (i) violates step 1, paragraph 1 of the adoption agency algorithm. -Line: 1 Col: 37 Unexpected end tag (u) in table context caused voodoo mode. -Line: 1 Col: 37 End tag (u) violates step 1, paragraph 1 of the adoption agency algorithm. -Line: 1 Col: 46 Unexpected end tag (strike) in table context caused voodoo mode. -Line: 1 Col: 46 End tag (strike) violates step 1, paragraph 1 of the adoption agency algorithm. -Line: 1 Col: 50 Unexpected end tag (s) in table context caused voodoo mode. -Line: 1 Col: 50 End tag (s) violates step 1, paragraph 1 of the adoption agency algorithm. -Line: 1 Col: 58 Unexpected end tag (blink) in table context caused voodoo mode. -Line: 1 Col: 58 Unexpected end tag (blink). Ignored. -Line: 1 Col: 63 Unexpected end tag (tt) in table context caused voodoo mode. -Line: 1 Col: 63 End tag (tt) violates step 1, paragraph 1 of the adoption agency algorithm. -Line: 1 Col: 69 Unexpected end tag (pre) in table context caused voodoo mode. -Line: 1 Col: 69 End tag (pre) seen too early. Expected other end tag. -Line: 1 Col: 75 Unexpected end tag (big) in table context caused voodoo mode. -Line: 1 Col: 75 End tag (big) violates step 1, paragraph 1 of the adoption agency algorithm. -Line: 1 Col: 83 Unexpected end tag (small) in table context caused voodoo mode. -Line: 1 Col: 83 End tag (small) violates step 1, paragraph 1 of the adoption agency algorithm. -Line: 1 Col: 90 Unexpected end tag (font) in table context caused voodoo mode. -Line: 1 Col: 90 End tag (font) violates step 1, paragraph 1 of the adoption agency algorithm. -Line: 1 Col: 99 Unexpected end tag (select) in table context caused voodoo mode. -Line: 1 Col: 99 Unexpected end tag (select). Ignored. -Line: 1 Col: 104 Unexpected end tag (h1) in table context caused voodoo mode. -Line: 1 Col: 104 End tag (h1) seen too early. Expected other end tag. -Line: 1 Col: 109 Unexpected end tag (h2) in table context caused voodoo mode. -Line: 1 Col: 109 End tag (h2) seen too early. Expected other end tag. -Line: 1 Col: 114 Unexpected end tag (h3) in table context caused voodoo mode. -Line: 1 Col: 114 End tag (h3) seen too early. Expected other end tag. -Line: 1 Col: 119 Unexpected end tag (h4) in table context caused voodoo mode. -Line: 1 Col: 119 End tag (h4) seen too early. Expected other end tag. -Line: 1 Col: 124 Unexpected end tag (h5) in table context caused voodoo mode. -Line: 1 Col: 124 End tag (h5) seen too early. Expected other end tag. -Line: 1 Col: 129 Unexpected end tag (h6) in table context caused voodoo mode. -Line: 1 Col: 129 End tag (h6) seen too early. Expected other end tag. -Line: 1 Col: 136 Unexpected end tag (body) in the table row phase. Ignored. -Line: 1 Col: 141 Unexpected end tag (br) in table context caused voodoo mode. -Line: 1 Col: 141 Unexpected end tag (br). Treated as br element. -Line: 1 Col: 145 Unexpected end tag (a) in table context caused voodoo mode. -Line: 1 Col: 145 End tag (a) violates step 1, paragraph 1 of the adoption agency algorithm. -Line: 1 Col: 151 Unexpected end tag (img) in table context caused voodoo mode. -Line: 1 Col: 151 This element (img) has no end tag. -Line: 1 Col: 159 Unexpected end tag (title) in table context caused voodoo mode. -Line: 1 Col: 159 Unexpected end tag (title). Ignored. -Line: 1 Col: 166 Unexpected end tag (span) in table context caused voodoo mode. -Line: 1 Col: 166 Unexpected end tag (span). Ignored. -Line: 1 Col: 174 Unexpected end tag (style) in table context caused voodoo mode. -Line: 1 Col: 174 Unexpected end tag (style). Ignored. -Line: 1 Col: 183 Unexpected end tag (script) in table context caused voodoo mode. -Line: 1 Col: 183 Unexpected end tag (script). Ignored. -Line: 1 Col: 196 Unexpected end tag (th). Ignored. -Line: 1 Col: 201 Unexpected end tag (td). Ignored. -Line: 1 Col: 206 Unexpected end tag (tr). Ignored. -Line: 1 Col: 214 This element (frame) has no end tag. -Line: 1 Col: 221 This element (area) has no end tag. -Line: 1 Col: 228 Unexpected end tag (link). Ignored. -Line: 1 Col: 236 This element (param) has no end tag. -Line: 1 Col: 241 This element (hr) has no end tag. -Line: 1 Col: 249 This element (input) has no end tag. -Line: 1 Col: 255 Unexpected end tag (col). Ignored. -Line: 1 Col: 262 Unexpected end tag (base). Ignored. -Line: 1 Col: 269 Unexpected end tag (meta). Ignored. -Line: 1 Col: 280 This element (basefont) has no end tag. -Line: 1 Col: 290 This element (bgsound) has no end tag. -Line: 1 Col: 298 This element (embed) has no end tag. -Line: 1 Col: 307 This element (spacer) has no end tag. -Line: 1 Col: 311 Unexpected end tag (p). Ignored. -Line: 1 Col: 316 End tag (dd) seen too early. Expected other end tag. -Line: 1 Col: 321 End tag (dt) seen too early. Expected other end tag. -Line: 1 Col: 331 Unexpected end tag (caption). Ignored. -Line: 1 Col: 342 Unexpected end tag (colgroup). Ignored. -Line: 1 Col: 350 Unexpected end tag (tbody). Ignored. -Line: 1 Col: 358 Unexpected end tag (tfoot). Ignored. -Line: 1 Col: 366 Unexpected end tag (thead). Ignored. -Line: 1 Col: 376 End tag (address) seen too early. Expected other end tag. -Line: 1 Col: 389 End tag (blockquote) seen too early. Expected other end tag. -Line: 1 Col: 398 End tag (center) seen too early. Expected other end tag. -Line: 1 Col: 404 Unexpected end tag (dir). Ignored. -Line: 1 Col: 410 End tag (div) seen too early. Expected other end tag. -Line: 1 Col: 415 End tag (dl) seen too early. Expected other end tag. -Line: 1 Col: 426 End tag (fieldset) seen too early. Expected other end tag. -Line: 1 Col: 436 End tag (listing) seen too early. Expected other end tag. -Line: 1 Col: 443 End tag (menu) seen too early. Expected other end tag. -Line: 1 Col: 448 End tag (ol) seen too early. Expected other end tag. -Line: 1 Col: 453 End tag (ul) seen too early. Expected other end tag. -Line: 1 Col: 458 End tag (li) seen too early. Expected other end tag. -Line: 1 Col: 465 End tag (nobr) violates step 1, paragraph 1 of the adoption agency algorithm. -Line: 1 Col: 471 This element (wbr) has no end tag. -Line: 1 Col: 487 End tag (button) seen too early. Expected other end tag. -Line: 1 Col: 497 End tag (marquee) seen too early. Expected other end tag. -Line: 1 Col: 506 End tag (object) seen too early. Expected other end tag. -Line: 1 Col: 524 Unexpected end tag (html). Ignored. -Line: 1 Col: 524 Unexpected end tag (frameset). Ignored. -Line: 1 Col: 531 Unexpected end tag (head). Ignored. -Line: 1 Col: 540 Unexpected end tag (iframe). Ignored. -Line: 1 Col: 548 This element (image) has no end tag. -Line: 1 Col: 558 This element (isindex) has no end tag. -Line: 1 Col: 568 Unexpected end tag (noembed). Ignored. -Line: 1 Col: 579 Unexpected end tag (noframes). Ignored. -Line: 1 Col: 590 Unexpected end tag (noscript). Ignored. -Line: 1 Col: 601 Unexpected end tag (optgroup). Ignored. -Line: 1 Col: 610 Unexpected end tag (option). Ignored. -Line: 1 Col: 622 Unexpected end tag (plaintext). Ignored. -Line: 1 Col: 633 Unexpected end tag (textarea). Ignored. -#document -| -| -| -|
    -| -| -| -|

    - -#data - -#errors -Line: 1 Col: 10 Unexpected start tag (frameset). Expected DOCTYPE. -Line: 1 Col: 10 Expected closing tag. Unexpected end of file. -#document -| -| -| diff --git a/html5lib/tests/testdata/tree-construction/tests10.dat b/html5lib/tests/testdata/tree-construction/tests10.dat deleted file mode 100644 index 4f8df86f..00000000 --- a/html5lib/tests/testdata/tree-construction/tests10.dat +++ /dev/null @@ -1,799 +0,0 @@ -#data - -#errors -#document -| -| -| -| -| - -#data -a -#errors -29: Bogus comment -#document -| -| -| -| -| -| - -#data - -#errors -#document -| -| -| -| -| - -#data - -#errors -35: Stray “svg†start tag. -42: Stray end tag “svg†-#document -| -| -| -| -| -#errors -43: Stray “svg†start tag. -50: Stray end tag “svg†-#document -| -| -| -| -|

    -#errors -34: Start tag “svg†seen in “tableâ€. -41: Stray end tag “svgâ€. -#document -| -| -| -| -| -| - -#data -
    foo
    -#errors -34: Start tag “svg†seen in “tableâ€. -46: Stray end tag “gâ€. -53: Stray end tag “svgâ€. -#document -| -| -| -| -| -| -| "foo" -| - -#data -
    foobar
    -#errors -34: Start tag “svg†seen in “tableâ€. -46: Stray end tag “gâ€. -58: Stray end tag “gâ€. -65: Stray end tag “svgâ€. -#document -| -| -| -| -| -| -| "foo" -| -| "bar" -| - -#data -
    foobar
    -#errors -41: Start tag “svg†seen in “tableâ€. -53: Stray end tag “gâ€. -65: Stray end tag “gâ€. -72: Stray end tag “svgâ€. -#document -| -| -| -| -| -| -| "foo" -| -| "bar" -| -| - -#data -
    foobar
    -#errors -45: Start tag “svg†seen in “tableâ€. -57: Stray end tag “gâ€. -69: Stray end tag “gâ€. -76: Stray end tag “svgâ€. -#document -| -| -| -| -| -| -| "foo" -| -| "bar" -| -| -| - -#data -
    foobar
    -#errors -#document -| -| -| -| -| -| -| -|
    -| -| -| "foo" -| -| "bar" - -#data -
    foobar

    baz

    -#errors -#document -| -| -| -| -| -| -| -|
    -| -| -| "foo" -| -| "bar" -|

    -| "baz" - -#data -
    foobar

    baz

    -#errors -#document -| -| -| -| -| -|
    -| -| -| "foo" -| -| "bar" -|

    -| "baz" - -#data -
    foobar

    baz

    quux -#errors -70: HTML start tag “p†in a foreign namespace context. -81: “table†closed but “caption†was still open. -#document -| -| -| -| -| -|
    -| -| -| "foo" -| -| "bar" -|

    -| "baz" -|

    -| "quux" - -#data -
    foobarbaz

    quux -#errors -78: “table†closed but “caption†was still open. -78: Unclosed elements on stack. -#document -| -| -| -| -| -|
    -| -| -| "foo" -| -| "bar" -| "baz" -|

    -| "quux" - -#data -foobar

    baz

    quux -#errors -44: Start tag “svg†seen in “tableâ€. -56: Stray end tag “gâ€. -68: Stray end tag “gâ€. -71: HTML start tag “p†in a foreign namespace context. -71: Start tag “p†seen in “tableâ€. -#document -| -| -| -| -| -| -| "foo" -| -| "bar" -|

    -| "baz" -| -| -|

    -| "quux" - -#data -

    quux -#errors -50: Stray “svg†start tag. -54: Stray “g†start tag. -62: Stray end tag “g†-66: Stray “g†start tag. -74: Stray end tag “g†-77: Stray “p†start tag. -88: “table†end tag with “select†open. -#document -| -| -| -| -| -| -| -|
    -|

    quux -#errors -36: Start tag “select†seen in “tableâ€. -42: Stray “svg†start tag. -46: Stray “g†start tag. -54: Stray end tag “g†-58: Stray “g†start tag. -66: Stray end tag “g†-69: Stray “p†start tag. -80: “table†end tag with “select†open. -#document -| -| -| -| -| -|

    -| "quux" - -#data -foobar

    baz -#errors -41: Stray “svg†start tag. -68: HTML start tag “p†in a foreign namespace context. -#document -| -| -| -| -| -| -| "foo" -| -| "bar" -|

    -| "baz" - -#data -foobar

    baz -#errors -34: Stray “svg†start tag. -61: HTML start tag “p†in a foreign namespace context. -#document -| -| -| -| -| -| -| "foo" -| -| "bar" -|

    -| "baz" - -#data -

    -#errors -31: Stray “svg†start tag. -35: Stray “g†start tag. -40: Stray end tag “g†-44: Stray “g†start tag. -49: Stray end tag “g†-52: Stray “p†start tag. -58: Stray “span†start tag. -58: End of file seen and there were open elements. -#document -| -| -| -| - -#data -

    -#errors -42: Stray “svg†start tag. -46: Stray “g†start tag. -51: Stray end tag “g†-55: Stray “g†start tag. -60: Stray end tag “g†-63: Stray “p†start tag. -69: Stray “span†start tag. -#document -| -| -| -| - -#data - -#errors -#document -| -| -| -| -| xlink:href="foo" -| -| xlink href="foo" - -#data - -#errors -#document -| -| -| -| -| xlink:href="foo" -| xml:lang="en" -| -| -| xlink href="foo" -| xml lang="en" - -#data - -#errors -#document -| -| -| -| -| xlink:href="foo" -| xml:lang="en" -| -| -| xlink href="foo" -| xml lang="en" - -#data -bar -#errors -#document -| -| -| -| -| xlink:href="foo" -| xml:lang="en" -| -| -| xlink href="foo" -| xml lang="en" -| "bar" - -#data - -#errors -#document -| -| -| -| - -#data -

    a -#errors -#document -| -| -| -|
    -| -| "a" - -#data -
    a -#errors -#document -| -| -| -|
    -| -| -| "a" - -#data -
    -#errors -#document -| -| -| -|
    -| -| -| - -#data -
    a -#errors -#document -| -| -| -|
    -| -| -| -| -| "a" - -#data -

    a -#errors -#document -| -| -| -|

    -| -| -| -|

    -| "a" - -#data -
      a -#errors -40: HTML start tag “ul†in a foreign namespace context. -41: End of file in a foreign namespace context. -#document -| -| -| -| -| -| -|
      -| -|
        -| "a" - -#data -
          a -#errors -35: HTML start tag “ul†in a foreign namespace context. -36: End of file in a foreign namespace context. -#document -| -| -| -| -| -| -| -|
            -| "a" - -#data -

            -#errors -#document -| -| -| -| -|

            -| -| -|

            - -#data -

            -#errors -#document -| -| -| -| -|

            -| -| -|

            - -#data -

            -#errors -#document -| -| -| -|

            -| -| -| -|

            -|

            - -#data -
            -#errors -#document -| -| -| -| -| -|
            -| -|
            -| -| - -#data -
            -#errors -#document -| -| -| -| -| -| -| -|
            -|
            -| - -#data - -#errors -#document -| -| -| -| -| -| - -#data -

    -#errors -#document -| -| -| -| -|
    -| -| - -#data - -#errors -#document -| -| -| -| -| -| - -#data - -#errors -#document -| -| -| -| -| -| - -#data - -#errors -#document -| -| -| -| -| -| - -#data - -#errors -#document -| -| -| -| -| -| - -#data - -#errors -#document -| -| -| -| -| -| - -#data - -#errors -#document -| -| -| -| -| -| - -#data - -#errors -#document -| -| -| -| -| -| - -#data - -#errors -#document -| -| -| -| -| -| - -#data - -#errors -#document -| -| -| -| -| -| - -#data - -#errors -#document -| -| -| -| -| -| - -#data - -#errors -#document -| -| -| -| -| -| -| - -#data -
    -#errors -#document -| -| -| -| -| -| -| -|
    -| -| -| -| -| - -#data - -#errors -#document -| -| -| -| -| -| -| -| -| -| -| -| -| -| diff --git a/html5lib/tests/testdata/tree-construction/tests11.dat b/html5lib/tests/testdata/tree-construction/tests11.dat deleted file mode 100644 index 638cde47..00000000 --- a/html5lib/tests/testdata/tree-construction/tests11.dat +++ /dev/null @@ -1,482 +0,0 @@ -#data - -#errors -#document -| -| -| -| -| -| attributeName="" -| attributeType="" -| baseFrequency="" -| baseProfile="" -| calcMode="" -| clipPathUnits="" -| contentScriptType="" -| contentStyleType="" -| diffuseConstant="" -| edgeMode="" -| externalResourcesRequired="" -| filterRes="" -| filterUnits="" -| glyphRef="" -| gradientTransform="" -| gradientUnits="" -| kernelMatrix="" -| kernelUnitLength="" -| keyPoints="" -| keySplines="" -| keyTimes="" -| lengthAdjust="" -| limitingConeAngle="" -| markerHeight="" -| markerUnits="" -| markerWidth="" -| maskContentUnits="" -| maskUnits="" -| numOctaves="" -| pathLength="" -| patternContentUnits="" -| patternTransform="" -| patternUnits="" -| pointsAtX="" -| pointsAtY="" -| pointsAtZ="" -| preserveAlpha="" -| preserveAspectRatio="" -| primitiveUnits="" -| refX="" -| refY="" -| repeatCount="" -| repeatDur="" -| requiredExtensions="" -| requiredFeatures="" -| specularConstant="" -| specularExponent="" -| spreadMethod="" -| startOffset="" -| stdDeviation="" -| stitchTiles="" -| surfaceScale="" -| systemLanguage="" -| tableValues="" -| targetX="" -| targetY="" -| textLength="" -| viewBox="" -| viewTarget="" -| xChannelSelector="" -| yChannelSelector="" -| zoomAndPan="" - -#data - -#errors -#document -| -| -| -| -| -| attributeName="" -| attributeType="" -| baseFrequency="" -| baseProfile="" -| calcMode="" -| clipPathUnits="" -| contentScriptType="" -| contentStyleType="" -| diffuseConstant="" -| edgeMode="" -| externalResourcesRequired="" -| filterRes="" -| filterUnits="" -| glyphRef="" -| gradientTransform="" -| gradientUnits="" -| kernelMatrix="" -| kernelUnitLength="" -| keyPoints="" -| keySplines="" -| keyTimes="" -| lengthAdjust="" -| limitingConeAngle="" -| markerHeight="" -| markerUnits="" -| markerWidth="" -| maskContentUnits="" -| maskUnits="" -| numOctaves="" -| pathLength="" -| patternContentUnits="" -| patternTransform="" -| patternUnits="" -| pointsAtX="" -| pointsAtY="" -| pointsAtZ="" -| preserveAlpha="" -| preserveAspectRatio="" -| primitiveUnits="" -| refX="" -| refY="" -| repeatCount="" -| repeatDur="" -| requiredExtensions="" -| requiredFeatures="" -| specularConstant="" -| specularExponent="" -| spreadMethod="" -| startOffset="" -| stdDeviation="" -| stitchTiles="" -| surfaceScale="" -| systemLanguage="" -| tableValues="" -| targetX="" -| targetY="" -| textLength="" -| viewBox="" -| viewTarget="" -| xChannelSelector="" -| yChannelSelector="" -| zoomAndPan="" - -#data - -#errors -#document -| -| -| -| -| -| attributeName="" -| attributeType="" -| baseFrequency="" -| baseProfile="" -| calcMode="" -| clipPathUnits="" -| contentScriptType="" -| contentStyleType="" -| diffuseConstant="" -| edgeMode="" -| externalResourcesRequired="" -| filterRes="" -| filterUnits="" -| glyphRef="" -| gradientTransform="" -| gradientUnits="" -| kernelMatrix="" -| kernelUnitLength="" -| keyPoints="" -| keySplines="" -| keyTimes="" -| lengthAdjust="" -| limitingConeAngle="" -| markerHeight="" -| markerUnits="" -| markerWidth="" -| maskContentUnits="" -| maskUnits="" -| numOctaves="" -| pathLength="" -| patternContentUnits="" -| patternTransform="" -| patternUnits="" -| pointsAtX="" -| pointsAtY="" -| pointsAtZ="" -| preserveAlpha="" -| preserveAspectRatio="" -| primitiveUnits="" -| refX="" -| refY="" -| repeatCount="" -| repeatDur="" -| requiredExtensions="" -| requiredFeatures="" -| specularConstant="" -| specularExponent="" -| spreadMethod="" -| startOffset="" -| stdDeviation="" -| stitchTiles="" -| surfaceScale="" -| systemLanguage="" -| tableValues="" -| targetX="" -| targetY="" -| textLength="" -| viewBox="" -| viewTarget="" -| xChannelSelector="" -| yChannelSelector="" -| zoomAndPan="" - -#data - -#errors -#document -| -| -| -| -| -| attributename="" -| attributetype="" -| basefrequency="" -| baseprofile="" -| calcmode="" -| clippathunits="" -| contentscripttype="" -| contentstyletype="" -| diffuseconstant="" -| edgemode="" -| externalresourcesrequired="" -| filterres="" -| filterunits="" -| glyphref="" -| gradienttransform="" -| gradientunits="" -| kernelmatrix="" -| kernelunitlength="" -| keypoints="" -| keysplines="" -| keytimes="" -| lengthadjust="" -| limitingconeangle="" -| markerheight="" -| markerunits="" -| markerwidth="" -| maskcontentunits="" -| maskunits="" -| numoctaves="" -| pathlength="" -| patterncontentunits="" -| patterntransform="" -| patternunits="" -| pointsatx="" -| pointsaty="" -| pointsatz="" -| preservealpha="" -| preserveaspectratio="" -| primitiveunits="" -| refx="" -| refy="" -| repeatcount="" -| repeatdur="" -| requiredextensions="" -| requiredfeatures="" -| specularconstant="" -| specularexponent="" -| spreadmethod="" -| startoffset="" -| stddeviation="" -| stitchtiles="" -| surfacescale="" -| systemlanguage="" -| tablevalues="" -| targetx="" -| targety="" -| textlength="" -| viewbox="" -| viewtarget="" -| xchannelselector="" -| ychannelselector="" -| zoomandpan="" - -#data - -#errors -#document -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| - -#data - -#errors -#document -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| - -#data - -#errors -#document -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| - -#data - -#errors -#document -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| -| - -#data - -#errors -#document -| -| -| -| -| -| diff --git a/html5lib/tests/testdata/tree-construction/tests12.dat b/html5lib/tests/testdata/tree-construction/tests12.dat deleted file mode 100644 index 63107d27..00000000 --- a/html5lib/tests/testdata/tree-construction/tests12.dat +++ /dev/null @@ -1,62 +0,0 @@ -#data -

    foobazeggs

    spam

    quuxbar -#errors -#document -| -| -| -| -|

    -| "foo" -| -| -| -| "baz" -| -| -| -| -| "eggs" -| -| -|

    -| "spam" -| -| -| -|
    -| -| -| "quux" -| "bar" - -#data -foobazeggs

    spam
    quuxbar -#errors -#document -| -| -| -| -| "foo" -| -| -| -| "baz" -| -| -| -| -| "eggs" -| -| -|

    -| "spam" -| -| -| -|
    -| -| -| "quux" -| "bar" diff --git a/html5lib/tests/testdata/tree-construction/tests14.dat b/html5lib/tests/testdata/tree-construction/tests14.dat deleted file mode 100644 index b8713f88..00000000 --- a/html5lib/tests/testdata/tree-construction/tests14.dat +++ /dev/null @@ -1,74 +0,0 @@ -#data - -#errors -#document -| -| -| -| -| - -#data - -#errors -#document -| -| -| -| -| -| - -#data - -#errors -15: Unexpected start tag html -#document -| -| -| abc:def="gh" -| -| -| - -#data - -#errors -15: Unexpected start tag html -#document -| -| -| xml:lang="bar" -| -| - -#data - -#errors -#document -| -| -| 123="456" -| -| - -#data - -#errors -#document -| -| -| 123="456" -| 789="012" -| -| - -#data - -#errors -#document -| -| -| -| -| 789="012" diff --git a/html5lib/tests/testdata/tree-construction/tests15.dat b/html5lib/tests/testdata/tree-construction/tests15.dat deleted file mode 100644 index 6ce1c0d1..00000000 --- a/html5lib/tests/testdata/tree-construction/tests15.dat +++ /dev/null @@ -1,208 +0,0 @@ -#data -

    X -#errors -Line: 1 Col: 31 Unexpected end tag (p). Ignored. -Line: 1 Col: 36 Expected closing tag. Unexpected end of file. -#document -| -| -| -| -|

    -| -| -| -| -| -| -| " " -|

    -| "X" - -#data -

    -

    X -#errors -Line: 1 Col: 3 Unexpected start tag (p). Expected DOCTYPE. -Line: 1 Col: 16 Unexpected end tag (p). Ignored. -Line: 2 Col: 4 Expected closing tag. Unexpected end of file. -#document -| -| -| -|

    -| -| -| -| -| -| -| " -" -|

    -| "X" - -#data - -#errors -Line: 1 Col: 22 Unexpected end tag (html) after the (implied) root element. -#document -| -| -| -| -| " " - -#data - -#errors -Line: 1 Col: 22 Unexpected end tag (body) after the (implied) root element. -#document -| -| -| -| -| - -#data - -#errors -Line: 1 Col: 6 Unexpected start tag (html). Expected DOCTYPE. -Line: 1 Col: 13 Unexpected end tag (html) after the (implied) root element. -#document -| -| -| -| - -#data -X -#errors -Line: 1 Col: 22 Unexpected end tag (body) after the (implied) root element. -#document -| -| -| -| -| -| "X" - -#data -<!doctype html><table> X<meta></table> -#errors -Line: 1 Col: 24 Unexpected non-space characters in table context caused voodoo mode. -Line: 1 Col: 30 Unexpected start tag (meta) in table context caused voodoo mode. -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| " X" -| <meta> -| <table> - -#data -<!doctype html><table> x</table> -#errors -Line: 1 Col: 24 Unexpected non-space characters in table context caused voodoo mode. -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| " x" -| <table> - -#data -<!doctype html><table> x </table> -#errors -Line: 1 Col: 25 Unexpected non-space characters in table context caused voodoo mode. -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| " x " -| <table> - -#data -<!doctype html><table><tr> x</table> -#errors -Line: 1 Col: 28 Unexpected non-space characters in table context caused voodoo mode. -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| " x" -| <table> -| <tbody> -| <tr> - -#data -<!doctype html><table>X<style> <tr>x </style> </table> -#errors -Line: 1 Col: 23 Unexpected non-space characters in table context caused voodoo mode. -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| "X" -| <table> -| <style> -| " <tr>x " -| " " - -#data -<!doctype html><div><table><a>foo</a> <tr><td>bar</td> </tr></table></div> -#errors -Line: 1 Col: 30 Unexpected start tag (a) in table context caused voodoo mode. -Line: 1 Col: 37 Unexpected end tag (a) in table context caused voodoo mode. -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <div> -| <a> -| "foo" -| <table> -| " " -| <tbody> -| <tr> -| <td> -| "bar" -| " " - -#data -<frame></frame></frame><frameset><frame><frameset><frame></frameset><noframes></frameset><noframes> -#errors -6: Start tag seen without seeing a doctype first. Expected “<!DOCTYPE html>â€. -13: Stray start tag “frameâ€. -21: Stray end tag “frameâ€. -29: Stray end tag “frameâ€. -39: “frameset†start tag after “body†already open. -105: End of file seen inside an [R]CDATA element. -105: End of file seen and there were open elements. -XXX: These errors are wrong, please fix me! -#document -| <html> -| <head> -| <frameset> -| <frame> -| <frameset> -| <frame> -| <noframes> -| "</frameset><noframes>" - -#data -<!DOCTYPE html><object></html> -#errors -1: Expected closing tag. Unexpected end of file -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <object> diff --git a/html5lib/tests/testdata/tree-construction/tests16.dat b/html5lib/tests/testdata/tree-construction/tests16.dat deleted file mode 100644 index c8ef66f0..00000000 --- a/html5lib/tests/testdata/tree-construction/tests16.dat +++ /dev/null @@ -1,2299 +0,0 @@ -#data -<!doctype html><script> -#errors -Line: 1 Col: 23 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| <body> - -#data -<!doctype html><script>a -#errors -Line: 1 Col: 24 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "a" -| <body> - -#data -<!doctype html><script>< -#errors -Line: 1 Col: 24 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "<" -| <body> - -#data -<!doctype html><script></ -#errors -Line: 1 Col: 25 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "</" -| <body> - -#data -<!doctype html><script></S -#errors -Line: 1 Col: 26 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "</S" -| <body> - -#data -<!doctype html><script></SC -#errors -Line: 1 Col: 27 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "</SC" -| <body> - -#data -<!doctype html><script></SCR -#errors -Line: 1 Col: 28 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "</SCR" -| <body> - -#data -<!doctype html><script></SCRI -#errors -Line: 1 Col: 29 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "</SCRI" -| <body> - -#data -<!doctype html><script></SCRIP -#errors -Line: 1 Col: 30 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "</SCRIP" -| <body> - -#data -<!doctype html><script></SCRIPT -#errors -Line: 1 Col: 31 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "</SCRIPT" -| <body> - -#data -<!doctype html><script></SCRIPT -#errors -Line: 1 Col: 32 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| <body> - -#data -<!doctype html><script></s -#errors -Line: 1 Col: 26 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "</s" -| <body> - -#data -<!doctype html><script></sc -#errors -Line: 1 Col: 27 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "</sc" -| <body> - -#data -<!doctype html><script></scr -#errors -Line: 1 Col: 28 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "</scr" -| <body> - -#data -<!doctype html><script></scri -#errors -Line: 1 Col: 29 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "</scri" -| <body> - -#data -<!doctype html><script></scrip -#errors -Line: 1 Col: 30 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "</scrip" -| <body> - -#data -<!doctype html><script></script -#errors -Line: 1 Col: 31 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "</script" -| <body> - -#data -<!doctype html><script></script -#errors -Line: 1 Col: 32 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| <body> - -#data -<!doctype html><script><! -#errors -Line: 1 Col: 25 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "<!" -| <body> - -#data -<!doctype html><script><!a -#errors -Line: 1 Col: 26 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "<!a" -| <body> - -#data -<!doctype html><script><!- -#errors -Line: 1 Col: 26 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "<!-" -| <body> - -#data -<!doctype html><script><!-a -#errors -Line: 1 Col: 27 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "<!-a" -| <body> - -#data -<!doctype html><script><!-- -#errors -Line: 1 Col: 27 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "<!--" -| <body> - -#data -<!doctype html><script><!--a -#errors -Line: 1 Col: 28 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "<!--a" -| <body> - -#data -<!doctype html><script><!--< -#errors -Line: 1 Col: 28 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "<!--<" -| <body> - -#data -<!doctype html><script><!--<a -#errors -Line: 1 Col: 29 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "<!--<a" -| <body> - -#data -<!doctype html><script><!--</ -#errors -Line: 1 Col: 27 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "<!--</" -| <body> - -#data -<!doctype html><script><!--</script -#errors -Line: 1 Col: 35 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "<!--</script" -| <body> - -#data -<!doctype html><script><!--</script -#errors -Line: 1 Col: 36 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "<!--" -| <body> - -#data -<!doctype html><script><!--<s -#errors -Line: 1 Col: 29 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "<!--<s" -| <body> - -#data -<!doctype html><script><!--<script -#errors -Line: 1 Col: 34 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "<!--<script" -| <body> - -#data -<!doctype html><script><!--<script -#errors -Line: 1 Col: 35 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "<!--<script " -| <body> - -#data -<!doctype html><script><!--<script < -#errors -Line: 1 Col: 36 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "<!--<script <" -| <body> - -#data -<!doctype html><script><!--<script <a -#errors -Line: 1 Col: 37 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "<!--<script <a" -| <body> - -#data -<!doctype html><script><!--<script </ -#errors -Line: 1 Col: 37 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "<!--<script </" -| <body> - -#data -<!doctype html><script><!--<script </s -#errors -Line: 1 Col: 38 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "<!--<script </s" -| <body> - -#data -<!doctype html><script><!--<script </script -#errors -Line: 1 Col: 43 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "<!--<script </script" -| <body> - -#data -<!doctype html><script><!--<script </scripta -#errors -Line: 1 Col: 44 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "<!--<script </scripta" -| <body> - -#data -<!doctype html><script><!--<script </script -#errors -Line: 1 Col: 44 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "<!--<script </script " -| <body> - -#data -<!doctype html><script><!--<script </script> -#errors -Line: 1 Col: 44 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "<!--<script </script>" -| <body> - -#data -<!doctype html><script><!--<script </script/ -#errors -Line: 1 Col: 44 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "<!--<script </script/" -| <body> - -#data -<!doctype html><script><!--<script </script < -#errors -Line: 1 Col: 45 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "<!--<script </script <" -| <body> - -#data -<!doctype html><script><!--<script </script <a -#errors -Line: 1 Col: 46 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "<!--<script </script <a" -| <body> - -#data -<!doctype html><script><!--<script </script </ -#errors -Line: 1 Col: 46 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "<!--<script </script </" -| <body> - -#data -<!doctype html><script><!--<script </script </script -#errors -Line: 1 Col: 52 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "<!--<script </script </script" -| <body> - -#data -<!doctype html><script><!--<script </script </script -#errors -Line: 1 Col: 53 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "<!--<script </script " -| <body> - -#data -<!doctype html><script><!--<script </script </script/ -#errors -Line: 1 Col: 53 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "<!--<script </script " -| <body> - -#data -<!doctype html><script><!--<script </script </script> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "<!--<script </script " -| <body> - -#data -<!doctype html><script><!--<script - -#errors -Line: 1 Col: 36 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "<!--<script -" -| <body> - -#data -<!doctype html><script><!--<script -a -#errors -Line: 1 Col: 37 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "<!--<script -a" -| <body> - -#data -<!doctype html><script><!--<script -< -#errors -Line: 1 Col: 37 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "<!--<script -<" -| <body> - -#data -<!doctype html><script><!--<script -- -#errors -Line: 1 Col: 37 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "<!--<script --" -| <body> - -#data -<!doctype html><script><!--<script --a -#errors -Line: 1 Col: 38 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "<!--<script --a" -| <body> - -#data -<!doctype html><script><!--<script --< -#errors -Line: 1 Col: 38 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "<!--<script --<" -| <body> - -#data -<!doctype html><script><!--<script --> -#errors -Line: 1 Col: 38 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "<!--<script -->" -| <body> - -#data -<!doctype html><script><!--<script -->< -#errors -Line: 1 Col: 39 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "<!--<script --><" -| <body> - -#data -<!doctype html><script><!--<script --></ -#errors -Line: 1 Col: 40 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "<!--<script --></" -| <body> - -#data -<!doctype html><script><!--<script --></script -#errors -Line: 1 Col: 46 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "<!--<script --></script" -| <body> - -#data -<!doctype html><script><!--<script --></script -#errors -Line: 1 Col: 47 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "<!--<script -->" -| <body> - -#data -<!doctype html><script><!--<script --></script/ -#errors -Line: 1 Col: 47 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "<!--<script -->" -| <body> - -#data -<!doctype html><script><!--<script --></script> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "<!--<script -->" -| <body> - -#data -<!doctype html><script><!--<script><\/script>--></script> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "<!--<script><\/script>-->" -| <body> - -#data -<!doctype html><script><!--<script></scr'+'ipt>--></script> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "<!--<script></scr'+'ipt>-->" -| <body> - -#data -<!doctype html><script><!--<script></script><script></script></script> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "<!--<script></script><script></script>" -| <body> - -#data -<!doctype html><script><!--<script></script><script></script>--><!--</script> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "<!--<script></script><script></script>--><!--" -| <body> - -#data -<!doctype html><script><!--<script></script><script></script>-- ></script> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "<!--<script></script><script></script>-- >" -| <body> - -#data -<!doctype html><script><!--<script></script><script></script>- -></script> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "<!--<script></script><script></script>- ->" -| <body> - -#data -<!doctype html><script><!--<script></script><script></script>- - ></script> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "<!--<script></script><script></script>- - >" -| <body> - -#data -<!doctype html><script><!--<script></script><script></script>-></script> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "<!--<script></script><script></script>->" -| <body> - -#data -<!doctype html><script><!--<script>--!></script>X -#errors -Line: 1 Col: 49 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "<!--<script>--!></script>X" -| <body> - -#data -<!doctype html><script><!--<scr'+'ipt></script>--></script> -#errors -Line: 1 Col: 59 Unexpected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "<!--<scr'+'ipt>" -| <body> -| "-->" - -#data -<!doctype html><script><!--<script></scr'+'ipt></script>X -#errors -Line: 1 Col: 57 Unexpected end of file. Expected end tag (script). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| "<!--<script></scr'+'ipt></script>X" -| <body> - -#data -<!doctype html><style><!--<style></style>--></style> -#errors -Line: 1 Col: 52 Unexpected end tag (style). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <style> -| "<!--<style>" -| <body> -| "-->" - -#data -<!doctype html><style><!--</style>X -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <style> -| "<!--" -| <body> -| "X" - -#data -<!doctype html><style><!--...</style>...--></style> -#errors -Line: 1 Col: 51 Unexpected end tag (style). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <style> -| "<!--..." -| <body> -| "...-->" - -#data -<!doctype html><style><!--<br><html xmlns:v="urn:schemas-microsoft-com:vml"><!--[if !mso]><style></style>X -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <style> -| "<!--<br><html xmlns:v="urn:schemas-microsoft-com:vml"><!--[if !mso]><style>" -| <body> -| "X" - -#data -<!doctype html><style><!--...<style><!--...--!></style>--></style> -#errors -Line: 1 Col: 66 Unexpected end tag (style). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <style> -| "<!--...<style><!--...--!>" -| <body> -| "-->" - -#data -<!doctype html><style><!--...</style><!-- --><style>@import ...</style> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <style> -| "<!--..." -| <!-- --> -| <style> -| "@import ..." -| <body> - -#data -<!doctype html><style>...<style><!--...</style><!-- --></style> -#errors -Line: 1 Col: 63 Unexpected end tag (style). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <style> -| "...<style><!--..." -| <!-- --> -| <body> - -#data -<!doctype html><style>...<!--[if IE]><style>...</style>X -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <style> -| "...<!--[if IE]><style>..." -| <body> -| "X" - -#data -<!doctype html><title><!--<title>--> -#errors -Line: 1 Col: 52 Unexpected end tag (title). -#document -| -| -| -| -| "<!--<title>" -| <body> -| "-->" - -#data -<!doctype html><title></title> -#errors -#document -| -| -| -| -| "" -| - -#data -foo/title><link></head><body>X -#errors -Line: 1 Col: 52 Unexpected end of file. Expected end tag (title). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <title> -| "foo/title><link></head><body>X" -| <body> - -#data -<!doctype html><noscript><!--<noscript></noscript>--></noscript> -#errors -Line: 1 Col: 64 Unexpected end tag (noscript). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <noscript> -| "<!--<noscript>" -| <body> -| "-->" - -#data -<!doctype html><noscript><!--</noscript>X<noscript>--></noscript> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <noscript> -| "<!--" -| <body> -| "X" -| <noscript> -| "-->" - -#data -<!doctype html><noscript><iframe></noscript>X -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <noscript> -| "<iframe>" -| <body> -| "X" - -#data -<!doctype html><noframes><!--<noframes></noframes>--></noframes> -#errors -Line: 1 Col: 64 Unexpected end tag (noframes). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <noframes> -| "<!--<noframes>" -| <body> -| "-->" - -#data -<!doctype html><noframes><body><script><!--...</script></body></noframes></html> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <noframes> -| "<body><script><!--...</script></body>" -| <body> - -#data -<!doctype html><textarea><!--<textarea></textarea>--></textarea> -#errors -Line: 1 Col: 64 Unexpected end tag (textarea). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <textarea> -| "<!--<textarea>" -| "-->" - -#data -<!doctype html><textarea></textarea></textarea> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <textarea> -| "</textarea>" - -#data -<!doctype html><textarea><</textarea> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <textarea> -| "<" - -#data -<!doctype html><textarea>a<b</textarea> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <textarea> -| "a<b" - -#data -<!doctype html><iframe><!--<iframe></iframe>--></iframe> -#errors -Line: 1 Col: 56 Unexpected end tag (iframe). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <iframe> -| "<!--<iframe>" -| "-->" - -#data -<!doctype html><iframe>...<!--X->...<!--/X->...</iframe> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <iframe> -| "...<!--X->...<!--/X->..." - -#data -<!doctype html><xmp><!--<xmp></xmp>--></xmp> -#errors -Line: 1 Col: 44 Unexpected end tag (xmp). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <xmp> -| "<!--<xmp>" -| "-->" - -#data -<!doctype html><noembed><!--<noembed></noembed>--></noembed> -#errors -Line: 1 Col: 60 Unexpected end tag (noembed). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <noembed> -| "<!--<noembed>" -| "-->" - -#data -<script> -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 8 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| <body> - -#data -<script>a -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 9 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| "a" -| <body> - -#data -<script>< -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 9 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| "<" -| <body> - -#data -<script></ -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 10 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| "</" -| <body> - -#data -<script></S -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 11 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| "</S" -| <body> - -#data -<script></SC -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 12 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| "</SC" -| <body> - -#data -<script></SCR -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 13 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| "</SCR" -| <body> - -#data -<script></SCRI -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 14 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| "</SCRI" -| <body> - -#data -<script></SCRIP -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 15 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| "</SCRIP" -| <body> - -#data -<script></SCRIPT -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 16 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| "</SCRIPT" -| <body> - -#data -<script></SCRIPT -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 17 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| <body> - -#data -<script></s -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 11 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| "</s" -| <body> - -#data -<script></sc -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 12 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| "</sc" -| <body> - -#data -<script></scr -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 13 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| "</scr" -| <body> - -#data -<script></scri -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 14 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| "</scri" -| <body> - -#data -<script></scrip -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 15 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| "</scrip" -| <body> - -#data -<script></script -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 16 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| "</script" -| <body> - -#data -<script></script -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 17 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| <body> - -#data -<script><! -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 10 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| "<!" -| <body> - -#data -<script><!a -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 11 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| "<!a" -| <body> - -#data -<script><!- -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 11 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| "<!-" -| <body> - -#data -<script><!-a -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 12 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| "<!-a" -| <body> - -#data -<script><!-- -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 12 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| "<!--" -| <body> - -#data -<script><!--a -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 13 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| "<!--a" -| <body> - -#data -<script><!--< -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 13 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| "<!--<" -| <body> - -#data -<script><!--<a -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 14 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| "<!--<a" -| <body> - -#data -<script><!--</ -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 14 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| "<!--</" -| <body> - -#data -<script><!--</script -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 20 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| "<!--</script" -| <body> - -#data -<script><!--</script -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 21 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| "<!--" -| <body> - -#data -<script><!--<s -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 14 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| "<!--<s" -| <body> - -#data -<script><!--<script -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 19 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| "<!--<script" -| <body> - -#data -<script><!--<script -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 20 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| "<!--<script " -| <body> - -#data -<script><!--<script < -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 21 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| "<!--<script <" -| <body> - -#data -<script><!--<script <a -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 22 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| "<!--<script <a" -| <body> - -#data -<script><!--<script </ -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 22 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| "<!--<script </" -| <body> - -#data -<script><!--<script </s -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 23 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| "<!--<script </s" -| <body> - -#data -<script><!--<script </script -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 28 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| "<!--<script </script" -| <body> - -#data -<script><!--<script </scripta -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 29 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| "<!--<script </scripta" -| <body> - -#data -<script><!--<script </script -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 29 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| "<!--<script </script " -| <body> - -#data -<script><!--<script </script> -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 29 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| "<!--<script </script>" -| <body> - -#data -<script><!--<script </script/ -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 29 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| "<!--<script </script/" -| <body> - -#data -<script><!--<script </script < -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 30 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| "<!--<script </script <" -| <body> - -#data -<script><!--<script </script <a -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 31 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| "<!--<script </script <a" -| <body> - -#data -<script><!--<script </script </ -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 31 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| "<!--<script </script </" -| <body> - -#data -<script><!--<script </script </script -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 38 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| "<!--<script </script </script" -| <body> - -#data -<script><!--<script </script </script -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 38 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| "<!--<script </script " -| <body> - -#data -<script><!--<script </script </script/ -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 38 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| "<!--<script </script " -| <body> - -#data -<script><!--<script </script </script> -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -#document -| <html> -| <head> -| <script> -| "<!--<script </script " -| <body> - -#data -<script><!--<script - -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 21 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| "<!--<script -" -| <body> - -#data -<script><!--<script -a -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 22 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| "<!--<script -a" -| <body> - -#data -<script><!--<script -- -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 22 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| "<!--<script --" -| <body> - -#data -<script><!--<script --a -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 23 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| "<!--<script --a" -| <body> - -#data -<script><!--<script --> -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 23 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| "<!--<script -->" -| <body> - -#data -<script><!--<script -->< -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 24 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| "<!--<script --><" -| <body> - -#data -<script><!--<script --></ -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 25 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| "<!--<script --></" -| <body> - -#data -<script><!--<script --></script -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 31 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| "<!--<script --></script" -| <body> - -#data -<script><!--<script --></script -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 32 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| "<!--<script -->" -| <body> - -#data -<script><!--<script --></script/ -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 32 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| "<!--<script -->" -| <body> - -#data -<script><!--<script --></script> -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -#document -| <html> -| <head> -| <script> -| "<!--<script -->" -| <body> - -#data -<script><!--<script><\/script>--></script> -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -#document -| <html> -| <head> -| <script> -| "<!--<script><\/script>-->" -| <body> - -#data -<script><!--<script></scr'+'ipt>--></script> -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -#document -| <html> -| <head> -| <script> -| "<!--<script></scr'+'ipt>-->" -| <body> - -#data -<script><!--<script></script><script></script></script> -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -#document -| <html> -| <head> -| <script> -| "<!--<script></script><script></script>" -| <body> - -#data -<script><!--<script></script><script></script>--><!--</script> -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -#document -| <html> -| <head> -| <script> -| "<!--<script></script><script></script>--><!--" -| <body> - -#data -<script><!--<script></script><script></script>-- ></script> -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -#document -| <html> -| <head> -| <script> -| "<!--<script></script><script></script>-- >" -| <body> - -#data -<script><!--<script></script><script></script>- -></script> -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -#document -| <html> -| <head> -| <script> -| "<!--<script></script><script></script>- ->" -| <body> - -#data -<script><!--<script></script><script></script>- - ></script> -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -#document -| <html> -| <head> -| <script> -| "<!--<script></script><script></script>- - >" -| <body> - -#data -<script><!--<script></script><script></script>-></script> -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -#document -| <html> -| <head> -| <script> -| "<!--<script></script><script></script>->" -| <body> - -#data -<script><!--<script>--!></script>X -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 34 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| "<!--<script>--!></script>X" -| <body> - -#data -<script><!--<scr'+'ipt></script>--></script> -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 44 Unexpected end tag (script). -#document -| <html> -| <head> -| <script> -| "<!--<scr'+'ipt>" -| <body> -| "-->" - -#data -<script><!--<script></scr'+'ipt></script>X -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 42 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| "<!--<script></scr'+'ipt></script>X" -| <body> - -#data -<style><!--<style></style>--></style> -#errors -Line: 1 Col: 7 Unexpected start tag (style). Expected DOCTYPE. -Line: 1 Col: 37 Unexpected end tag (style). -#document -| <html> -| <head> -| <style> -| "<!--<style>" -| <body> -| "-->" - -#data -<style><!--</style>X -#errors -Line: 1 Col: 7 Unexpected start tag (style). Expected DOCTYPE. -#document -| <html> -| <head> -| <style> -| "<!--" -| <body> -| "X" - -#data -<style><!--...</style>...--></style> -#errors -Line: 1 Col: 7 Unexpected start tag (style). Expected DOCTYPE. -Line: 1 Col: 36 Unexpected end tag (style). -#document -| <html> -| <head> -| <style> -| "<!--..." -| <body> -| "...-->" - -#data -<style><!--<br><html xmlns:v="urn:schemas-microsoft-com:vml"><!--[if !mso]><style></style>X -#errors -Line: 1 Col: 7 Unexpected start tag (style). Expected DOCTYPE. -#document -| <html> -| <head> -| <style> -| "<!--<br><html xmlns:v="urn:schemas-microsoft-com:vml"><!--[if !mso]><style>" -| <body> -| "X" - -#data -<style><!--...<style><!--...--!></style>--></style> -#errors -Line: 1 Col: 7 Unexpected start tag (style). Expected DOCTYPE. -Line: 1 Col: 51 Unexpected end tag (style). -#document -| <html> -| <head> -| <style> -| "<!--...<style><!--...--!>" -| <body> -| "-->" - -#data -<style><!--...</style><!-- --><style>@import ...</style> -#errors -Line: 1 Col: 7 Unexpected start tag (style). Expected DOCTYPE. -#document -| <html> -| <head> -| <style> -| "<!--..." -| <!-- --> -| <style> -| "@import ..." -| <body> - -#data -<style>...<style><!--...</style><!-- --></style> -#errors -Line: 1 Col: 7 Unexpected start tag (style). Expected DOCTYPE. -Line: 1 Col: 48 Unexpected end tag (style). -#document -| <html> -| <head> -| <style> -| "...<style><!--..." -| <!-- --> -| <body> - -#data -<style>...<!--[if IE]><style>...</style>X -#errors -Line: 1 Col: 7 Unexpected start tag (style). Expected DOCTYPE. -#document -| <html> -| <head> -| <style> -| "...<!--[if IE]><style>..." -| <body> -| "X" - -#data -<title><!--<title>--> -#errors -Line: 1 Col: 7 Unexpected start tag (title). Expected DOCTYPE. -Line: 1 Col: 37 Unexpected end tag (title). -#document -| -| -| -| "<!--<title>" -| <body> -| "-->" - -#data -<title></title> -#errors -Line: 1 Col: 7 Unexpected start tag (title). Expected DOCTYPE. -#document -| -| -| -| "" -| - -#data -foo/title><link></head><body>X -#errors -Line: 1 Col: 7 Unexpected start tag (title). Expected DOCTYPE. -Line: 1 Col: 37 Unexpected end of file. Expected end tag (title). -#document -| <html> -| <head> -| <title> -| "foo/title><link></head><body>X" -| <body> - -#data -<noscript><!--<noscript></noscript>--></noscript> -#errors -Line: 1 Col: 10 Unexpected start tag (noscript). Expected DOCTYPE. -Line: 1 Col: 49 Unexpected end tag (noscript). -#document -| <html> -| <head> -| <noscript> -| "<!--<noscript>" -| <body> -| "-->" - -#data -<noscript><!--</noscript>X<noscript>--></noscript> -#errors -Line: 1 Col: 10 Unexpected start tag (noscript). Expected DOCTYPE. -#document -| <html> -| <head> -| <noscript> -| "<!--" -| <body> -| "X" -| <noscript> -| "-->" - -#data -<noscript><iframe></noscript>X -#errors -Line: 1 Col: 10 Unexpected start tag (noscript). Expected DOCTYPE. -#document -| <html> -| <head> -| <noscript> -| "<iframe>" -| <body> -| "X" - -#data -<noframes><!--<noframes></noframes>--></noframes> -#errors -Line: 1 Col: 10 Unexpected start tag (noframes). Expected DOCTYPE. -Line: 1 Col: 49 Unexpected end tag (noframes). -#document -| <html> -| <head> -| <noframes> -| "<!--<noframes>" -| <body> -| "-->" - -#data -<noframes><body><script><!--...</script></body></noframes></html> -#errors -Line: 1 Col: 10 Unexpected start tag (noframes). Expected DOCTYPE. -#document -| <html> -| <head> -| <noframes> -| "<body><script><!--...</script></body>" -| <body> - -#data -<textarea><!--<textarea></textarea>--></textarea> -#errors -Line: 1 Col: 10 Unexpected start tag (textarea). Expected DOCTYPE. -Line: 1 Col: 49 Unexpected end tag (textarea). -#document -| <html> -| <head> -| <body> -| <textarea> -| "<!--<textarea>" -| "-->" - -#data -<textarea></textarea></textarea> -#errors -Line: 1 Col: 10 Unexpected start tag (textarea). Expected DOCTYPE. -#document -| <html> -| <head> -| <body> -| <textarea> -| "</textarea>" - -#data -<iframe><!--<iframe></iframe>--></iframe> -#errors -Line: 1 Col: 8 Unexpected start tag (iframe). Expected DOCTYPE. -Line: 1 Col: 41 Unexpected end tag (iframe). -#document -| <html> -| <head> -| <body> -| <iframe> -| "<!--<iframe>" -| "-->" - -#data -<iframe>...<!--X->...<!--/X->...</iframe> -#errors -Line: 1 Col: 8 Unexpected start tag (iframe). Expected DOCTYPE. -#document -| <html> -| <head> -| <body> -| <iframe> -| "...<!--X->...<!--/X->..." - -#data -<xmp><!--<xmp></xmp>--></xmp> -#errors -Line: 1 Col: 5 Unexpected start tag (xmp). Expected DOCTYPE. -Line: 1 Col: 29 Unexpected end tag (xmp). -#document -| <html> -| <head> -| <body> -| <xmp> -| "<!--<xmp>" -| "-->" - -#data -<noembed><!--<noembed></noembed>--></noembed> -#errors -Line: 1 Col: 9 Unexpected start tag (noembed). Expected DOCTYPE. -Line: 1 Col: 45 Unexpected end tag (noembed). -#document -| <html> -| <head> -| <body> -| <noembed> -| "<!--<noembed>" -| "-->" - -#data -<!doctype html><table> - -#errors -Line 2 Col 0 Unexpected end of file. Expected table content. -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <table> -| " -" - -#data -<!doctype html><table><td><span><font></span><span> -#errors -Line 1 Col 26 Unexpected table cell start tag (td) in the table body phase. -Line 1 Col 45 Unexpected end tag (span). -Line 1 Col 51 Expected closing tag. Unexpected end of file. -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <table> -| <tbody> -| <tr> -| <td> -| <span> -| <font> -| <font> -| <span> - -#data -<!doctype html><form><table></form><form></table></form> -#errors -35: Stray end tag “formâ€. -41: Start tag “form†seen in “tableâ€. -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <form> -| <table> -| <form> diff --git a/html5lib/tests/testdata/tree-construction/tests17.dat b/html5lib/tests/testdata/tree-construction/tests17.dat deleted file mode 100644 index 7b555f88..00000000 --- a/html5lib/tests/testdata/tree-construction/tests17.dat +++ /dev/null @@ -1,153 +0,0 @@ -#data -<!doctype html><table><tbody><select><tr> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <select> -| <table> -| <tbody> -| <tr> - -#data -<!doctype html><table><tr><select><td> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <select> -| <table> -| <tbody> -| <tr> -| <td> - -#data -<!doctype html><table><tr><td><select><td> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <table> -| <tbody> -| <tr> -| <td> -| <select> -| <td> - -#data -<!doctype html><table><tr><th><select><td> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <table> -| <tbody> -| <tr> -| <th> -| <select> -| <td> - -#data -<!doctype html><table><caption><select><tr> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <table> -| <caption> -| <select> -| <tbody> -| <tr> - -#data -<!doctype html><select><tr> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <select> - -#data -<!doctype html><select><td> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <select> - -#data -<!doctype html><select><th> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <select> - -#data -<!doctype html><select><tbody> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <select> - -#data -<!doctype html><select><thead> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <select> - -#data -<!doctype html><select><tfoot> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <select> - -#data -<!doctype html><select><caption> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <select> - -#data -<!doctype html><table><tr></table>a -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <table> -| <tbody> -| <tr> -| "a" diff --git a/html5lib/tests/testdata/tree-construction/tests18.dat b/html5lib/tests/testdata/tree-construction/tests18.dat deleted file mode 100644 index 680e1f06..00000000 --- a/html5lib/tests/testdata/tree-construction/tests18.dat +++ /dev/null @@ -1,269 +0,0 @@ -#data -<!doctype html><plaintext></plaintext> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <plaintext> -| "</plaintext>" - -#data -<!doctype html><table><plaintext></plaintext> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <plaintext> -| "</plaintext>" -| <table> - -#data -<!doctype html><table><tbody><plaintext></plaintext> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <plaintext> -| "</plaintext>" -| <table> -| <tbody> - -#data -<!doctype html><table><tbody><tr><plaintext></plaintext> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <plaintext> -| "</plaintext>" -| <table> -| <tbody> -| <tr> - -#data -<!doctype html><table><tbody><tr><plaintext></plaintext> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <plaintext> -| "</plaintext>" -| <table> -| <tbody> -| <tr> - -#data -<!doctype html><table><td><plaintext></plaintext> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <table> -| <tbody> -| <tr> -| <td> -| <plaintext> -| "</plaintext>" - -#data -<!doctype html><table><caption><plaintext></plaintext> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <table> -| <caption> -| <plaintext> -| "</plaintext>" - -#data -<!doctype html><table><tr><style></script></style>abc -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| "abc" -| <table> -| <tbody> -| <tr> -| <style> -| "</script>" - -#data -<!doctype html><table><tr><script></style></script>abc -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| "abc" -| <table> -| <tbody> -| <tr> -| <script> -| "</style>" - -#data -<!doctype html><table><caption><style></script></style>abc -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <table> -| <caption> -| <style> -| "</script>" -| "abc" - -#data -<!doctype html><table><td><style></script></style>abc -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <table> -| <tbody> -| <tr> -| <td> -| <style> -| "</script>" -| "abc" - -#data -<!doctype html><select><script></style></script>abc -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <select> -| <script> -| "</style>" -| "abc" - -#data -<!doctype html><table><select><script></style></script>abc -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <select> -| <script> -| "</style>" -| "abc" -| <table> - -#data -<!doctype html><table><tr><select><script></style></script>abc -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <select> -| <script> -| "</style>" -| "abc" -| <table> -| <tbody> -| <tr> - -#data -<!doctype html><frameset></frameset><noframes>abc -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <frameset> -| <noframes> -| "abc" - -#data -<!doctype html><frameset></frameset><noframes>abc</noframes><!--abc--> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <frameset> -| <noframes> -| "abc" -| <!-- abc --> - -#data -<!doctype html><frameset></frameset></html><noframes>abc -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <frameset> -| <noframes> -| "abc" - -#data -<!doctype html><frameset></frameset></html><noframes>abc</noframes><!--abc--> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <frameset> -| <noframes> -| "abc" -| <!-- abc --> - -#data -<!doctype html><table><tr></tbody><tfoot> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <table> -| <tbody> -| <tr> -| <tfoot> - -#data -<!doctype html><table><td><svg></svg>abc<td> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <table> -| <tbody> -| <tr> -| <td> -| <svg svg> -| "abc" -| <td> diff --git a/html5lib/tests/testdata/tree-construction/tests19.dat b/html5lib/tests/testdata/tree-construction/tests19.dat deleted file mode 100644 index 0d62f5a5..00000000 --- a/html5lib/tests/testdata/tree-construction/tests19.dat +++ /dev/null @@ -1,1237 +0,0 @@ -#data -<!doctype html><math><mn DefinitionUrl="foo"> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <math math> -| <math mn> -| definitionURL="foo" - -#data -<!doctype html><html></p><!--foo--> -#errors -#document -| <!DOCTYPE html> -| <html> -| <!-- foo --> -| <head> -| <body> - -#data -<!doctype html><head></head></p><!--foo--> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <!-- foo --> -| <body> - -#data -<!doctype html><body><p><pre> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <p> -| <pre> - -#data -<!doctype html><body><p><listing> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <p> -| <listing> - -#data -<!doctype html><p><plaintext> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <p> -| <plaintext> - -#data -<!doctype html><p><h1> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <p> -| <h1> - -#data -<!doctype html><form><isindex> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <form> - -#data -<!doctype html><isindex action="POST"> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <form> -| action="POST" -| <hr> -| <label> -| "This is a searchable index. Enter search keywords: " -| <input> -| name="isindex" -| <hr> - -#data -<!doctype html><isindex prompt="this is isindex"> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <form> -| <hr> -| <label> -| "this is isindex" -| <input> -| name="isindex" -| <hr> - -#data -<!doctype html><isindex type="hidden"> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <form> -| <hr> -| <label> -| "This is a searchable index. Enter search keywords: " -| <input> -| name="isindex" -| type="hidden" -| <hr> - -#data -<!doctype html><isindex name="foo"> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <form> -| <hr> -| <label> -| "This is a searchable index. Enter search keywords: " -| <input> -| name="isindex" -| <hr> - -#data -<!doctype html><ruby><p><rp> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <ruby> -| <p> -| <rp> - -#data -<!doctype html><ruby><div><span><rp> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <ruby> -| <div> -| <span> -| <rp> - -#data -<!doctype html><ruby><div><p><rp> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <ruby> -| <div> -| <p> -| <rp> - -#data -<!doctype html><ruby><p><rt> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <ruby> -| <p> -| <rt> - -#data -<!doctype html><ruby><div><span><rt> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <ruby> -| <div> -| <span> -| <rt> - -#data -<!doctype html><ruby><div><p><rt> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <ruby> -| <div> -| <p> -| <rt> - -#data -<!doctype html><math/><foo> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <math math> -| <foo> - -#data -<!doctype html><svg/><foo> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <svg svg> -| <foo> - -#data -<!doctype html><div></body><!--foo--> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <div> -| <!-- foo --> - -#data -<!doctype html><h1><div><h3><span></h1>foo -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <h1> -| <div> -| <h3> -| <span> -| "foo" - -#data -<!doctype html><p></h3>foo -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <p> -| "foo" - -#data -<!doctype html><h3><li>abc</h2>foo -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <h3> -| <li> -| "abc" -| "foo" - -#data -<!doctype html><table>abc<!--foo--> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| "abc" -| <table> -| <!-- foo --> - -#data -<!doctype html><table> <!--foo--> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <table> -| " " -| <!-- foo --> - -#data -<!doctype html><table> b <!--foo--> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| " b " -| <table> -| <!-- foo --> - -#data -<!doctype html><select><option><option> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <select> -| <option> -| <option> - -#data -<!doctype html><select><option></optgroup> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <select> -| <option> - -#data -<!doctype html><select><option></optgroup> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <select> -| <option> - -#data -<!doctype html><p><math><mi><p><h1> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <p> -| <math math> -| <math mi> -| <p> -| <h1> - -#data -<!doctype html><p><math><mo><p><h1> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <p> -| <math math> -| <math mo> -| <p> -| <h1> - -#data -<!doctype html><p><math><mn><p><h1> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <p> -| <math math> -| <math mn> -| <p> -| <h1> - -#data -<!doctype html><p><math><ms><p><h1> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <p> -| <math math> -| <math ms> -| <p> -| <h1> - -#data -<!doctype html><p><math><mtext><p><h1> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <p> -| <math math> -| <math mtext> -| <p> -| <h1> - -#data -<!doctype html><frameset></noframes> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <frameset> - -#data -<!doctype html><html c=d><body></html><html a=b> -#errors -#document -| <!DOCTYPE html> -| <html> -| a="b" -| c="d" -| <head> -| <body> - -#data -<!doctype html><html c=d><frameset></frameset></html><html a=b> -#errors -#document -| <!DOCTYPE html> -| <html> -| a="b" -| c="d" -| <head> -| <frameset> - -#data -<!doctype html><html><frameset></frameset></html><!--foo--> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <frameset> -| <!-- foo --> - -#data -<!doctype html><html><frameset></frameset></html> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <frameset> -| " " - -#data -<!doctype html><html><frameset></frameset></html>abc -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <frameset> - -#data -<!doctype html><html><frameset></frameset></html><p> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <frameset> - -#data -<!doctype html><html><frameset></frameset></html></p> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <frameset> - -#data -<html><frameset></frameset></html><!doctype html> -#errors -#document -| <html> -| <head> -| <frameset> - -#data -<!doctype html><body><frameset> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> - -#data -<!doctype html><p><frameset><frame> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <frameset> -| <frame> - -#data -<!doctype html><p>a<frameset> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <p> -| "a" - -#data -<!doctype html><p> <frameset><frame> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <frameset> -| <frame> - -#data -<!doctype html><pre><frameset> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <pre> - -#data -<!doctype html><listing><frameset> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <listing> - -#data -<!doctype html><li><frameset> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <li> - -#data -<!doctype html><dd><frameset> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <dd> - -#data -<!doctype html><dt><frameset> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <dt> - -#data -<!doctype html><button><frameset> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <button> - -#data -<!doctype html><applet><frameset> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <applet> - -#data -<!doctype html><marquee><frameset> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <marquee> - -#data -<!doctype html><object><frameset> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <object> - -#data -<!doctype html><table><frameset> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <table> - -#data -<!doctype html><area><frameset> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <area> - -#data -<!doctype html><basefont><frameset> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <basefont> -| <frameset> - -#data -<!doctype html><bgsound><frameset> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <bgsound> -| <frameset> - -#data -<!doctype html><br><frameset> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <br> - -#data -<!doctype html><embed><frameset> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <embed> - -#data -<!doctype html><img><frameset> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <img> - -#data -<!doctype html><input><frameset> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <input> - -#data -<!doctype html><keygen><frameset> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <keygen> - -#data -<!doctype html><wbr><frameset> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <wbr> - -#data -<!doctype html><hr><frameset> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <hr> - -#data -<!doctype html><textarea></textarea><frameset> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <textarea> - -#data -<!doctype html><xmp></xmp><frameset> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <xmp> - -#data -<!doctype html><iframe></iframe><frameset> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <iframe> - -#data -<!doctype html><select></select><frameset> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <select> - -#data -<!doctype html><svg></svg><frameset><frame> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <frameset> -| <frame> - -#data -<!doctype html><math></math><frameset><frame> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <frameset> -| <frame> - -#data -<!doctype html><svg><foreignObject><div> <frameset><frame> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <frameset> -| <frame> - -#data -<!doctype html><svg>a</svg><frameset><frame> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <svg svg> -| "a" - -#data -<!doctype html><svg> </svg><frameset><frame> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <frameset> -| <frame> - -#data -<html>aaa<frameset></frameset> -#errors -#document -| <html> -| <head> -| <body> -| "aaa" - -#data -<html> a <frameset></frameset> -#errors -#document -| <html> -| <head> -| <body> -| "a " - -#data -<!doctype html><div><frameset> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <frameset> - -#data -<!doctype html><div><body><frameset> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <div> - -#data -<!doctype html><p><math></p>a -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <p> -| <math math> -| "a" - -#data -<!doctype html><p><math><mn><span></p>a -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <p> -| <math math> -| <math mn> -| <span> -| <p> -| "a" - -#data -<!doctype html><math></html> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <math math> - -#data -<!doctype html><meta charset="ascii"> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <meta> -| charset="ascii" -| <body> - -#data -<!doctype html><meta http-equiv="content-type" content="text/html;charset=ascii"> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <meta> -| content="text/html;charset=ascii" -| http-equiv="content-type" -| <body> - -#data -<!doctype html><head><!--aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa--><meta charset="utf8"> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <!-- aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa --> -| <meta> -| charset="utf8" -| <body> - -#data -<!doctype html><html a=b><head></head><html c=d> -#errors -#document -| <!DOCTYPE html> -| <html> -| a="b" -| c="d" -| <head> -| <body> - -#data -<!doctype html><image/> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <img> - -#data -<!doctype html>a<i>b<table>c<b>d</i>e</b>f -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| "a" -| <i> -| "bc" -| <b> -| "de" -| "f" -| <table> - -#data -<!doctype html><table><i>a<b>b<div>c<a>d</i>e</b>f -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <i> -| "a" -| <b> -| "b" -| <b> -| <div> -| <b> -| <i> -| "c" -| <a> -| "d" -| <a> -| "e" -| <a> -| "f" -| <table> - -#data -<!doctype html><i>a<b>b<div>c<a>d</i>e</b>f -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <i> -| "a" -| <b> -| "b" -| <b> -| <div> -| <b> -| <i> -| "c" -| <a> -| "d" -| <a> -| "e" -| <a> -| "f" - -#data -<!doctype html><table><i>a<b>b<div>c</i> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <i> -| "a" -| <b> -| "b" -| <b> -| <div> -| <i> -| "c" -| <table> - -#data -<!doctype html><table><i>a<b>b<div>c<a>d</i>e</b>f -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <i> -| "a" -| <b> -| "b" -| <b> -| <div> -| <b> -| <i> -| "c" -| <a> -| "d" -| <a> -| "e" -| <a> -| "f" -| <table> - -#data -<!doctype html><table><i>a<div>b<tr>c<b>d</i>e -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <i> -| "a" -| <div> -| "b" -| <i> -| "c" -| <b> -| "d" -| <b> -| "e" -| <table> -| <tbody> -| <tr> - -#data -<!doctype html><table><td><table><i>a<div>b<b>c</i>d -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <table> -| <tbody> -| <tr> -| <td> -| <i> -| "a" -| <div> -| <i> -| "b" -| <b> -| "c" -| <b> -| "d" -| <table> - -#data -<!doctype html><body><bgsound> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <bgsound> - -#data -<!doctype html><body><basefont> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <basefont> - -#data -<!doctype html><a><b></a><basefont> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <a> -| <b> -| <basefont> - -#data -<!doctype html><a><b></a><bgsound> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <a> -| <b> -| <bgsound> - -#data -<!doctype html><figcaption><article></figcaption>a -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <figcaption> -| <article> -| "a" - -#data -<!doctype html><summary><article></summary>a -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <summary> -| <article> -| "a" - -#data -<!doctype html><p><a><plaintext>b -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <p> -| <a> -| <plaintext> -| <a> -| "b" - -#data -<!DOCTYPE html><div>a<a></div>b<p>c</p>d -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <div> -| "a" -| <a> -| <a> -| "b" -| <p> -| "c" -| "d" diff --git a/html5lib/tests/testdata/tree-construction/tests2.dat b/html5lib/tests/testdata/tree-construction/tests2.dat deleted file mode 100644 index 60d85922..00000000 --- a/html5lib/tests/testdata/tree-construction/tests2.dat +++ /dev/null @@ -1,763 +0,0 @@ -#data -<!DOCTYPE html>Test -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| "Test" - -#data -<textarea>test</div>test -#errors -Line: 1 Col: 10 Unexpected start tag (textarea). Expected DOCTYPE. -Line: 1 Col: 24 Expected closing tag. Unexpected end of file. -#document -| <html> -| <head> -| <body> -| <textarea> -| "test</div>test" - -#data -<table><td> -#errors -Line: 1 Col: 7 Unexpected start tag (table). Expected DOCTYPE. -Line: 1 Col: 11 Unexpected table cell start tag (td) in the table body phase. -Line: 1 Col: 11 Expected closing tag. Unexpected end of file. -#document -| <html> -| <head> -| <body> -| <table> -| <tbody> -| <tr> -| <td> - -#data -<table><td>test</tbody></table> -#errors -Line: 1 Col: 7 Unexpected start tag (table). Expected DOCTYPE. -Line: 1 Col: 11 Unexpected table cell start tag (td) in the table body phase. -#document -| <html> -| <head> -| <body> -| <table> -| <tbody> -| <tr> -| <td> -| "test" - -#data -<frame>test -#errors -Line: 1 Col: 7 Unexpected start tag (frame). Expected DOCTYPE. -Line: 1 Col: 7 Unexpected start tag frame. Ignored. -#document -| <html> -| <head> -| <body> -| "test" - -#data -<!DOCTYPE html><frameset>test -#errors -Line: 1 Col: 29 Unepxected characters in the frameset phase. Characters ignored. -Line: 1 Col: 29 Expected closing tag. Unexpected end of file. -#document -| <!DOCTYPE html> -| <html> -| <head> -| <frameset> - -#data -<!DOCTYPE html><frameset><!DOCTYPE html> -#errors -Line: 1 Col: 40 Unexpected DOCTYPE. Ignored. -Line: 1 Col: 40 Expected closing tag. Unexpected end of file. -#document -| <!DOCTYPE html> -| <html> -| <head> -| <frameset> - -#data -<!DOCTYPE html><font><p><b>test</font> -#errors -Line: 1 Col: 38 End tag (font) violates step 1, paragraph 3 of the adoption agency algorithm. -Line: 1 Col: 38 End tag (font) violates step 1, paragraph 3 of the adoption agency algorithm. -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <font> -| <p> -| <font> -| <b> -| "test" - -#data -<!DOCTYPE html><dt><div><dd> -#errors -Line: 1 Col: 28 Missing end tag (div, dt). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <dt> -| <div> -| <dd> - -#data -<script></x -#errors -Line: 1 Col: 8 Unexpected start tag (script). Expected DOCTYPE. -Line: 1 Col: 11 Unexpected end of file. Expected end tag (script). -#document -| <html> -| <head> -| <script> -| "</x" -| <body> - -#data -<table><plaintext><td> -#errors -Line: 1 Col: 7 Unexpected start tag (table). Expected DOCTYPE. -Line: 1 Col: 18 Unexpected start tag (plaintext) in table context caused voodoo mode. -Line: 1 Col: 22 Unexpected end of file. Expected table content. -#document -| <html> -| <head> -| <body> -| <plaintext> -| "<td>" -| <table> - -#data -<plaintext></plaintext> -#errors -Line: 1 Col: 11 Unexpected start tag (plaintext). Expected DOCTYPE. -Line: 1 Col: 23 Expected closing tag. Unexpected end of file. -#document -| <html> -| <head> -| <body> -| <plaintext> -| "</plaintext>" - -#data -<!DOCTYPE html><table><tr>TEST -#errors -Line: 1 Col: 30 Unexpected non-space characters in table context caused voodoo mode. -Line: 1 Col: 30 Unexpected end of file. Expected table content. -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| "TEST" -| <table> -| <tbody> -| <tr> - -#data -<!DOCTYPE html><body t1=1><body t2=2><body t3=3 t4=4> -#errors -Line: 1 Col: 37 Unexpected start tag (body). -Line: 1 Col: 53 Unexpected start tag (body). -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| t1="1" -| t2="2" -| t3="3" -| t4="4" - -#data -</b test -#errors -Line: 1 Col: 8 Unexpected end of file in attribute name. -Line: 1 Col: 8 End tag contains unexpected attributes. -Line: 1 Col: 8 Unexpected end tag (b). Expected DOCTYPE. -Line: 1 Col: 8 Unexpected end tag (b) after the (implied) root element. -#document -| <html> -| <head> -| <body> - -#data -<!DOCTYPE html></b test<b &=&>X -#errors -Line: 1 Col: 32 Named entity didn't end with ';'. -Line: 1 Col: 33 End tag contains unexpected attributes. -Line: 1 Col: 33 Unexpected end tag (b) after the (implied) root element. -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| "X" - -#data -<!doctypehtml><scrIPt type=text/x-foobar;baz>X</SCRipt -#errors -Line: 1 Col: 9 No space after literal string 'DOCTYPE'. -Line: 1 Col: 54 Unexpected end of file in the tag name. -#document -| <!DOCTYPE html> -| <html> -| <head> -| <script> -| type="text/x-foobar;baz" -| "X</SCRipt" -| <body> - -#data -& -#errors -Line: 1 Col: 1 Unexpected non-space characters. Expected DOCTYPE. -#document -| <html> -| <head> -| <body> -| "&" - -#data -&# -#errors -Line: 1 Col: 1 Numeric entity expected. Got end of file instead. -Line: 1 Col: 1 Unexpected non-space characters. Expected DOCTYPE. -#document -| <html> -| <head> -| <body> -| "&#" - -#data -&#X -#errors -Line: 1 Col: 3 Numeric entity expected but none found. -Line: 1 Col: 3 Unexpected non-space characters. Expected DOCTYPE. -#document -| <html> -| <head> -| <body> -| "&#X" - -#data -&#x -#errors -Line: 1 Col: 3 Numeric entity expected but none found. -Line: 1 Col: 3 Unexpected non-space characters. Expected DOCTYPE. -#document -| <html> -| <head> -| <body> -| "&#x" - -#data -- -#errors -Line: 1 Col: 4 Numeric entity didn't end with ';'. -Line: 1 Col: 4 Unexpected non-space characters. Expected DOCTYPE. -#document -| <html> -| <head> -| <body> -| "-" - -#data -&x-test -#errors -Line: 1 Col: 1 Named entity expected. Got none. -Line: 1 Col: 1 Unexpected non-space characters. Expected DOCTYPE. -#document -| <html> -| <head> -| <body> -| "&x-test" - -#data -<!doctypehtml><p><li> -#errors -Line: 1 Col: 9 No space after literal string 'DOCTYPE'. -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <p> -| <li> - -#data -<!doctypehtml><p><dt> -#errors -Line: 1 Col: 9 No space after literal string 'DOCTYPE'. -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <p> -| <dt> - -#data -<!doctypehtml><p><dd> -#errors -Line: 1 Col: 9 No space after literal string 'DOCTYPE'. -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <p> -| <dd> - -#data -<!doctypehtml><p><form> -#errors -Line: 1 Col: 9 No space after literal string 'DOCTYPE'. -Line: 1 Col: 23 Expected closing tag. Unexpected end of file. -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <p> -| <form> - -#data -<!DOCTYPE html><p></P>X -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <p> -| "X" - -#data -& -#errors -Line: 1 Col: 4 Named entity didn't end with ';'. -Line: 1 Col: 4 Unexpected non-space characters. Expected DOCTYPE. -#document -| <html> -| <head> -| <body> -| "&" - -#data -&AMp; -#errors -Line: 1 Col: 1 Named entity expected. Got none. -Line: 1 Col: 1 Unexpected non-space characters. Expected DOCTYPE. -#document -| <html> -| <head> -| <body> -| "&AMp;" - -#data -<!DOCTYPE html><html><head></head><body><thisISasillyTESTelementNameToMakeSureCrazyTagNamesArePARSEDcorrectLY> -#errors -Line: 1 Col: 110 Expected closing tag. Unexpected end of file. -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <thisisasillytestelementnametomakesurecrazytagnamesareparsedcorrectly> - -#data -<!DOCTYPE html>X</body>X -#errors -Line: 1 Col: 24 Unexpected non-space characters in the after body phase. -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| "XX" - -#data -<!DOCTYPE html><!-- X -#errors -Line: 1 Col: 21 Unexpected end of file in comment. -#document -| <!DOCTYPE html> -| <!-- X --> -| <html> -| <head> -| <body> - -#data -<!DOCTYPE html><table><caption>test TEST</caption><td>test -#errors -Line: 1 Col: 54 Unexpected table cell start tag (td) in the table body phase. -Line: 1 Col: 58 Expected closing tag. Unexpected end of file. -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <table> -| <caption> -| "test TEST" -| <tbody> -| <tr> -| <td> -| "test" - -#data -<!DOCTYPE html><select><option><optgroup> -#errors -Line: 1 Col: 41 Expected closing tag. Unexpected end of file. -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <select> -| <option> -| <optgroup> - -#data -<!DOCTYPE html><select><optgroup><option></optgroup><option><select><option> -#errors -Line: 1 Col: 68 Unexpected select start tag in the select phase treated as select end tag. -Line: 1 Col: 76 Expected closing tag. Unexpected end of file. -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <select> -| <optgroup> -| <option> -| <option> -| <option> - -#data -<!DOCTYPE html><select><optgroup><option><optgroup> -#errors -Line: 1 Col: 51 Expected closing tag. Unexpected end of file. -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <select> -| <optgroup> -| <option> -| <optgroup> - -#data -<!DOCTYPE html><datalist><option>foo</datalist>bar -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <datalist> -| <option> -| "foo" -| "bar" - -#data -<!DOCTYPE html><font><input><input></font> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <font> -| <input> -| <input> - -#data -<!DOCTYPE html><!-- XXX - XXX --> -#errors -#document -| <!DOCTYPE html> -| <!-- XXX - XXX --> -| <html> -| <head> -| <body> - -#data -<!DOCTYPE html><!-- XXX - XXX -#errors -Line: 1 Col: 29 Unexpected end of file in comment (-) -#document -| <!DOCTYPE html> -| <!-- XXX - XXX --> -| <html> -| <head> -| <body> - -#data -<!DOCTYPE html><!-- XXX - XXX - XXX --> -#errors -#document -| <!DOCTYPE html> -| <!-- XXX - XXX - XXX --> -| <html> -| <head> -| <body> - -#data -<isindex test=x name=x> -#errors -Line: 1 Col: 23 Unexpected start tag (isindex). Expected DOCTYPE. -Line: 1 Col: 23 Unexpected start tag isindex. Don't use it! -#document -| <html> -| <head> -| <body> -| <form> -| <hr> -| <label> -| "This is a searchable index. Enter search keywords: " -| <input> -| name="isindex" -| test="x" -| <hr> - -#data -test -test -#errors -Line: 2 Col: 4 Unexpected non-space characters. Expected DOCTYPE. -#document -| <html> -| <head> -| <body> -| "test -test" - -#data -<!DOCTYPE html><body><title>test</body> -#errors -#document -| -| -| -| -| -| "test</body>" - -#data -<!DOCTYPE html><body><title>X -#errors -#document -| -| -| -| -| -| "X" -| <meta> -| name="z" -| <link> -| rel="foo" -| <style> -| " -x { content:"</style" } " - -#data -<!DOCTYPE html><select><optgroup></optgroup></select> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> -| <select> -| <optgroup> - -#data - - -#errors -Line: 2 Col: 1 Unexpected End of file. Expected DOCTYPE. -#document -| <html> -| <head> -| <body> - -#data -<!DOCTYPE html> <html> -#errors -#document -| <!DOCTYPE html> -| <html> -| <head> -| <body> - -#data -<!DOCTYPE html><script> -</script> <title>x -#errors -#document -| -| -| -| -#errors -Line: 1 Col: 6 Unexpected start tag (head). Expected DOCTYPE. -Line: 1 Col: 21 Unexpected start tag (script) that can be in head. Moved. -#document -| -| -| -#errors -Line: 1 Col: 6 Unexpected start tag (head). Expected DOCTYPE. -Line: 1 Col: 28 Unexpected start tag (style) that can be in head. Moved. -#document -| -| -| -#errors -Line: 1 Col: 6 Unexpected start tag (head). Expected DOCTYPE. -#document -| -| -| -| -| "x" -| x -#errors -Line: 1 Col: 7 Unexpected start tag (style). Expected DOCTYPE. -Line: 1 Col: 22 Unexpected end of file. Expected end tag (style). -#document -| -| -| --> x -#errors -Line: 1 Col: 7 Unexpected start tag (style). Expected DOCTYPE. -#document -| -| -| x -#errors -Line: 1 Col: 7 Unexpected start tag (style). Expected DOCTYPE. -#document -| -| -| x -#errors -Line: 1 Col: 7 Unexpected start tag (style). Expected DOCTYPE. -#document -| -| -| x -#errors -Line: 1 Col: 7 Unexpected start tag (style). Expected DOCTYPE. -#document -| -| -|

    -#errors -#document -| -| -| -| -| -| ddd -#errors -#document -| -| -| -#errors -#document -| -| -| -| -|
  • -| -|