From f6439049d8d8b5a4709f1b78afbfd289d00e8c25 Mon Sep 17 00:00:00 2001 From: Davide Romanini Date: Mon, 16 Feb 2015 13:27:21 +0100 Subject: [PATCH] Squashed 'comicapi/' content from commit b7d2458 git-subtree-dir: comicapi git-subtree-split: b7d2458b80467a47be1d1d58b31ffcac62c2743c --- UnRAR2/UnRARDLL/license.txt | 18 + UnRAR2/UnRARDLL/unrar.dll | Bin 0 -> 165376 bytes UnRAR2/UnRARDLL/unrar.h | 140 ++++ UnRAR2/UnRARDLL/unrar.lib | Bin 0 -> 4114 bytes UnRAR2/UnRARDLL/unrardll.txt | 606 +++++++++++++++++ UnRAR2/UnRARDLL/whatsnew.txt | 80 +++ UnRAR2/UnRARDLL/x64/readme.txt | 1 + UnRAR2/UnRARDLL/x64/unrar64.dll | Bin 0 -> 191488 bytes UnRAR2/UnRARDLL/x64/unrar64.lib | Bin 0 -> 3972 bytes UnRAR2/__init__.py | 177 +++++ UnRAR2/rar_exceptions.py | 30 + UnRAR2/test_UnRAR2.py | 138 ++++ UnRAR2/unix.py | 218 +++++++ UnRAR2/windows.py | 309 +++++++++ __init__.py | 1 + comet.py | 260 ++++++++ comicarchive.py | 1088 +++++++++++++++++++++++++++++++ comicbookinfo.py | 152 +++++ comicinfoxml.py | 293 +++++++++ filenameparser.py | 277 ++++++++ genericmetadata.py | 316 +++++++++ issuestring.py | 140 ++++ utils.py | 597 +++++++++++++++++ 23 files changed, 4841 insertions(+) create mode 100644 UnRAR2/UnRARDLL/license.txt create mode 100644 UnRAR2/UnRARDLL/unrar.dll create mode 100644 UnRAR2/UnRARDLL/unrar.h create mode 100644 UnRAR2/UnRARDLL/unrar.lib create mode 100644 UnRAR2/UnRARDLL/unrardll.txt create mode 100644 UnRAR2/UnRARDLL/whatsnew.txt create mode 100644 UnRAR2/UnRARDLL/x64/readme.txt create mode 100644 UnRAR2/UnRARDLL/x64/unrar64.dll create mode 100644 UnRAR2/UnRARDLL/x64/unrar64.lib create mode 100644 UnRAR2/__init__.py create mode 100644 UnRAR2/rar_exceptions.py create mode 100644 UnRAR2/test_UnRAR2.py create mode 100644 UnRAR2/unix.py create mode 100644 UnRAR2/windows.py create mode 100644 __init__.py create mode 100644 comet.py create mode 100644 comicarchive.py create mode 100644 comicbookinfo.py create mode 100644 comicinfoxml.py create mode 100644 filenameparser.py create mode 100644 genericmetadata.py create mode 100644 issuestring.py create mode 100644 utils.py diff --git a/UnRAR2/UnRARDLL/license.txt b/UnRAR2/UnRARDLL/license.txt new file mode 100644 index 0000000..0c1540e --- /dev/null +++ b/UnRAR2/UnRARDLL/license.txt @@ -0,0 +1,18 @@ + The unrar.dll library is freeware. This means: + + 1. All copyrights to RAR and the unrar.dll are exclusively + owned by the author - Alexander Roshal. + + 2. The unrar.dll library may be used in any software to handle RAR + archives without limitations free of charge. + + 3. THE RAR ARCHIVER AND THE UNRAR.DLL LIBRARY ARE DISTRIBUTED "AS IS". + NO WARRANTY OF ANY KIND IS EXPRESSED OR IMPLIED. YOU USE AT + YOUR OWN RISK. THE AUTHOR WILL NOT BE LIABLE FOR DATA LOSS, + DAMAGES, LOSS OF PROFITS OR ANY OTHER KIND OF LOSS WHILE USING + OR MISUSING THIS SOFTWARE. + + Thank you for your interest in RAR and unrar.dll. + + + Alexander L. Roshal \ No newline at end of file diff --git a/UnRAR2/UnRARDLL/unrar.dll b/UnRAR2/UnRARDLL/unrar.dll new file mode 100644 index 0000000000000000000000000000000000000000..9757bf3d692d8668ce892c4bee814eb5394adf37 GIT binary patch literal 165376 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 Zpd|w>8EDBsO9omp(2{|c4E$3W_-_?5`fC6H literal 0 HcmV?d00001 diff --git a/UnRAR2/UnRARDLL/unrar.h b/UnRAR2/UnRARDLL/unrar.h new file mode 100644 index 0000000..7643fa7 --- /dev/null +++ b/UnRAR2/UnRARDLL/unrar.h @@ -0,0 +1,140 @@ +#ifndef _UNRAR_DLL_ +#define _UNRAR_DLL_ + +#define ERAR_END_ARCHIVE 10 +#define ERAR_NO_MEMORY 11 +#define ERAR_BAD_DATA 12 +#define ERAR_BAD_ARCHIVE 13 +#define ERAR_UNKNOWN_FORMAT 14 +#define ERAR_EOPEN 15 +#define ERAR_ECREATE 16 +#define ERAR_ECLOSE 17 +#define ERAR_EREAD 18 +#define ERAR_EWRITE 19 +#define ERAR_SMALL_BUF 20 +#define ERAR_UNKNOWN 21 +#define ERAR_MISSING_PASSWORD 22 + +#define RAR_OM_LIST 0 +#define RAR_OM_EXTRACT 1 +#define RAR_OM_LIST_INCSPLIT 2 + +#define RAR_SKIP 0 +#define RAR_TEST 1 +#define RAR_EXTRACT 2 + +#define RAR_VOL_ASK 0 +#define RAR_VOL_NOTIFY 1 + +#define RAR_DLL_VERSION 4 + +#ifdef _UNIX +#define CALLBACK +#define PASCAL +#define LONG long +#define HANDLE void * +#define LPARAM long +#define UINT unsigned int +#endif + +struct RARHeaderData +{ + char ArcName[260]; + char FileName[260]; + unsigned int Flags; + unsigned int PackSize; + unsigned int UnpSize; + unsigned int HostOS; + unsigned int FileCRC; + unsigned int FileTime; + unsigned int UnpVer; + unsigned int Method; + unsigned int FileAttr; + char *CmtBuf; + unsigned int CmtBufSize; + unsigned int CmtSize; + unsigned int CmtState; +}; + + +struct RARHeaderDataEx +{ + char ArcName[1024]; + wchar_t ArcNameW[1024]; + char FileName[1024]; + wchar_t FileNameW[1024]; + unsigned int Flags; + unsigned int PackSize; + unsigned int PackSizeHigh; + unsigned int UnpSize; + unsigned int UnpSizeHigh; + unsigned int HostOS; + unsigned int FileCRC; + unsigned int FileTime; + unsigned int UnpVer; + unsigned int Method; + unsigned int FileAttr; + char *CmtBuf; + unsigned int CmtBufSize; + unsigned int CmtSize; + unsigned int CmtState; + unsigned int Reserved[1024]; +}; + + +struct RAROpenArchiveData +{ + char *ArcName; + unsigned int OpenMode; + unsigned int OpenResult; + char *CmtBuf; + unsigned int CmtBufSize; + unsigned int CmtSize; + unsigned int CmtState; +}; + +struct RAROpenArchiveDataEx +{ + char *ArcName; + wchar_t *ArcNameW; + unsigned int OpenMode; + unsigned int OpenResult; + char *CmtBuf; + unsigned int CmtBufSize; + unsigned int CmtSize; + unsigned int CmtState; + unsigned int Flags; + unsigned int Reserved[32]; +}; + +enum UNRARCALLBACK_MESSAGES { + UCM_CHANGEVOLUME,UCM_PROCESSDATA,UCM_NEEDPASSWORD +}; + +typedef int (CALLBACK *UNRARCALLBACK)(UINT msg,LPARAM UserData,LPARAM P1,LPARAM P2); + +typedef int (PASCAL *CHANGEVOLPROC)(char *ArcName,int Mode); +typedef int (PASCAL *PROCESSDATAPROC)(unsigned char *Addr,int Size); + +#ifdef __cplusplus +extern "C" { +#endif + +HANDLE PASCAL RAROpenArchive(struct RAROpenArchiveData *ArchiveData); +HANDLE PASCAL RAROpenArchiveEx(struct RAROpenArchiveDataEx *ArchiveData); +int PASCAL RARCloseArchive(HANDLE hArcData); +int PASCAL RARReadHeader(HANDLE hArcData,struct RARHeaderData *HeaderData); +int PASCAL RARReadHeaderEx(HANDLE hArcData,struct RARHeaderDataEx *HeaderData); +int PASCAL RARProcessFile(HANDLE hArcData,int Operation,char *DestPath,char *DestName); +int PASCAL RARProcessFileW(HANDLE hArcData,int Operation,wchar_t *DestPath,wchar_t *DestName); +void PASCAL RARSetCallback(HANDLE hArcData,UNRARCALLBACK Callback,LPARAM UserData); +void PASCAL RARSetChangeVolProc(HANDLE hArcData,CHANGEVOLPROC ChangeVolProc); +void PASCAL RARSetProcessDataProc(HANDLE hArcData,PROCESSDATAPROC ProcessDataProc); +void PASCAL RARSetPassword(HANDLE hArcData,char *Password); +int PASCAL RARGetDllVersion(); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/UnRAR2/UnRARDLL/unrar.lib b/UnRAR2/UnRARDLL/unrar.lib new file mode 100644 index 0000000000000000000000000000000000000000..0f6b3146b8ec5bd83698122653a75bcb1f2caf70 GIT binary patch literal 4114 zcmcInOK%fN5dLhFhn=T@+wLMGkQOc`amG%NR?FnI5XB_X#DU{5*aNW`;>Zr*8-HML zd*Q%x;YaK>;<|@D?B2N`{sm&IW_o&h+GA%dLQ6H>T~*y*cXd^F&DCF=PUG;`!mVPw zES9S))g_~PdnwLe5Z&davS>Xj0QdnIUjrtaK>iId^&z0?62MgW9MDV;@aYrPMAL5r znxQ$EW-URdR1?j;6I7;}XsXU++gtbdcCEU-vAMr)ZSB=}E&Ih$$LYYfcMfW`elcGA z@<3X@cd)Z-i=VOy)#?y-BcN;YV{bWMY1Xgxo+6Zmn>&E6p0KtkH%w4+SmTCs;v|hq5Q}k6xBIHyY3eY03ZFFZ zx+fc+_rUFRTkRurLD_;X896SDC@$tGFxJL_<|ObY4}6#cO4Gn+^7P&e@QLUx^$S#6 zv%o3QI~r6bs*^4S6~-}#3m8Kl1x#QPQ<%mqW{^R4pe*P6b%LAen@j2DWH4cH=}d8! z^o%ndHaMl2_XySiKZu_jIZVRQ_s9EL*FhBJx|L-3_t{EHQd}6?^`Ki%PNfL6xQkm- z4v5&=1)ztX9FY`bs!)xL7(ci^ln5Mfhx#{bsp)wlRL*)ijFpOfIck|4Hvju`dm=+` z2YEY{OsVNUe)07Be$WN(P~-QoBWe@#Yo%6`ZinmiDg@;+ReuwG6#X34CKgVGURAIu zj({&jp&s*16i>5M&r_Un$;(asj7#$q#NpYvusq+pc)!)?w7cymC&e4q&0=k9XWN%* zABt^%AWr}aV^9ds(|62oNeq~c_VZ&}XTJ9bzJ3kCSf2|oEQ@fvCfyUvISe`e#&~(T zkYlh8F(REx#9{sw{)obJ0n4JtRTew+J--3gkftw+ zK8lAdg>=Obk^Q|ACe<1m zhiOKj>CaIFCtE3_O9q#Q_9LNX1zP-xlL(Nllvu-dmg~pzpG}D|GMX{u)Gi1#<;CSh zHv&7?Qyc3?^WXOfPPS57(pXOR$RI}yJTgiSDE*ZHqx<79J5Gq5MOc0!@}1Bo1)7%K zd;?lV{?EoE`zm>VUP05+(QiN;7H@?JQOUz1Fxg7!C6zF>(qj7>?T-H(IN?vsp(PLs F{{t7Z#m)c# literal 0 HcmV?d00001 diff --git a/UnRAR2/UnRARDLL/unrardll.txt b/UnRAR2/UnRARDLL/unrardll.txt new file mode 100644 index 0000000..291c871 --- /dev/null +++ b/UnRAR2/UnRARDLL/unrardll.txt @@ -0,0 +1,606 @@ + + UnRAR.dll Manual + ~~~~~~~~~~~~~~~~ + + UnRAR.dll is a 32-bit Windows dynamic-link library which provides + file extraction from RAR archives. + + + Exported functions + +==================================================================== +HANDLE PASCAL RAROpenArchive(struct RAROpenArchiveData *ArchiveData) +==================================================================== + +Description +~~~~~~~~~~~ + Open RAR archive and allocate memory structures + +Parameters +~~~~~~~~~~ +ArchiveData Points to RAROpenArchiveData structure + +struct RAROpenArchiveData +{ + char *ArcName; + UINT OpenMode; + UINT OpenResult; + char *CmtBuf; + UINT CmtBufSize; + UINT CmtSize; + UINT CmtState; +}; + +Structure fields: + +ArcName + Input parameter which should point to zero terminated string + containing the archive name. + +OpenMode + Input parameter. + + Possible values + + RAR_OM_LIST + Open archive for reading file headers only. + + RAR_OM_EXTRACT + Open archive for testing and extracting files. + + RAR_OM_LIST_INCSPLIT + Open archive for reading file headers only. If you open an archive + in such mode, RARReadHeader[Ex] will return all file headers, + including those with "file continued from previous volume" flag. + In case of RAR_OM_LIST such headers are automatically skipped. + So if you process RAR volumes in RAR_OM_LIST_INCSPLIT mode, you will + get several file header records for same file if file is split between + volumes. For such files only the last file header record will contain + the correct file CRC and if you wish to get the correct packed size, + you need to sum up packed sizes of all parts. + +OpenResult + Output parameter. + + Possible values + + 0 Success + ERAR_NO_MEMORY Not enough memory to initialize data structures + ERAR_BAD_DATA Archive header broken + ERAR_BAD_ARCHIVE File is not valid RAR archive + ERAR_UNKNOWN_FORMAT Unknown encryption used for archive headers + ERAR_EOPEN File open error + +CmtBuf + Input parameter which should point to the buffer for archive + comments. Maximum comment size is limited to 64Kb. Comment text is + zero terminated. If the comment text is larger than the buffer + size, the comment text will be truncated. If CmtBuf is set to + NULL, comments will not be read. + +CmtBufSize + Input parameter which should contain size of buffer for archive + comments. + +CmtSize + Output parameter containing size of comments actually read into the + buffer, cannot exceed CmtBufSize. + +CmtState + Output parameter. + + Possible values + + 0 comments not present + 1 Comments read completely + ERAR_NO_MEMORY Not enough memory to extract comments + ERAR_BAD_DATA Broken comment + ERAR_UNKNOWN_FORMAT Unknown comment format + ERAR_SMALL_BUF Buffer too small, comments not completely read + +Return values +~~~~~~~~~~~~~ + Archive handle or NULL in case of error + + +======================================================================== +HANDLE PASCAL RAROpenArchiveEx(struct RAROpenArchiveDataEx *ArchiveData) +======================================================================== + +Description +~~~~~~~~~~~ + Similar to RAROpenArchive, but uses RAROpenArchiveDataEx structure + allowing to specify Unicode archive name and returning information + about archive flags. + +Parameters +~~~~~~~~~~ +ArchiveData Points to RAROpenArchiveDataEx structure + +struct RAROpenArchiveDataEx +{ + char *ArcName; + wchar_t *ArcNameW; + unsigned int OpenMode; + unsigned int OpenResult; + char *CmtBuf; + unsigned int CmtBufSize; + unsigned int CmtSize; + unsigned int CmtState; + unsigned int Flags; + unsigned int Reserved[32]; +}; + +Structure fields: + +ArcNameW + Input parameter which should point to zero terminated Unicode string + containing the archive name or NULL if Unicode name is not specified. + +Flags + Output parameter. Combination of bit flags. + + Possible values + + 0x0001 - Volume attribute (archive volume) + 0x0002 - Archive comment present + 0x0004 - Archive lock attribute + 0x0008 - Solid attribute (solid archive) + 0x0010 - New volume naming scheme ('volname.partN.rar') + 0x0020 - Authenticity information present + 0x0040 - Recovery record present + 0x0080 - Block headers are encrypted + 0x0100 - First volume (set only by RAR 3.0 and later) + +Reserved[32] + Reserved for future use. Must be zero. + +Information on other structure fields and function return values +is available above, in RAROpenArchive function description. + + +==================================================================== +int PASCAL RARCloseArchive(HANDLE hArcData) +==================================================================== + +Description +~~~~~~~~~~~ + Close RAR archive and release allocated memory. It must be called when + archive processing is finished, even if the archive processing was stopped + due to an error. + +Parameters +~~~~~~~~~~ +hArcData + This parameter should contain the archive handle obtained from the + RAROpenArchive function call. + +Return values +~~~~~~~~~~~~~ + 0 Success + ERAR_ECLOSE Archive close error + + +==================================================================== +int PASCAL RARReadHeader(HANDLE hArcData, + struct RARHeaderData *HeaderData) +==================================================================== + +Description +~~~~~~~~~~~ + Read header of file in archive. + +Parameters +~~~~~~~~~~ +hArcData + This parameter should contain the archive handle obtained from the + RAROpenArchive function call. + +HeaderData + It should point to RARHeaderData structure: + +struct RARHeaderData +{ + char ArcName[260]; + char FileName[260]; + UINT Flags; + UINT PackSize; + UINT UnpSize; + UINT HostOS; + UINT FileCRC; + UINT FileTime; + UINT UnpVer; + UINT Method; + UINT FileAttr; + char *CmtBuf; + UINT CmtBufSize; + UINT CmtSize; + UINT CmtState; +}; + +Structure fields: + +ArcName + Output parameter which contains a zero terminated string of the + current archive name. May be used to determine the current volume + name. + +FileName + Output parameter which contains a zero terminated string of the + file name in OEM (DOS) encoding. + +Flags + Output parameter which contains file flags: + + 0x01 - file continued from previous volume + 0x02 - file continued on next volume + 0x04 - file encrypted with password + 0x08 - file comment present + 0x10 - compression of previous files is used (solid flag) + + bits 7 6 5 + + 0 0 0 - dictionary size 64 Kb + 0 0 1 - dictionary size 128 Kb + 0 1 0 - dictionary size 256 Kb + 0 1 1 - dictionary size 512 Kb + 1 0 0 - dictionary size 1024 Kb + 1 0 1 - dictionary size 2048 KB + 1 1 0 - dictionary size 4096 KB + 1 1 1 - file is directory + + Other bits are reserved. + +PackSize + Output parameter means packed file size or size of the + file part if file was split between volumes. + +UnpSize + Output parameter - unpacked file size. + +HostOS + Output parameter - operating system used for archiving: + + 0 - MS DOS; + 1 - OS/2. + 2 - Win32 + 3 - Unix + +FileCRC + Output parameter which contains unpacked file CRC. In case of file parts + split between volumes only the last part contains the correct CRC + and it is accessible only in RAR_OM_LIST_INCSPLIT listing mode. + +FileTime + Output parameter - contains date and time in standard MS DOS format. + +UnpVer + Output parameter - RAR version needed to extract file. + It is encoded as 10 * Major version + minor version. + +Method + Output parameter - packing method. + +FileAttr + Output parameter - file attributes. + +CmtBuf + File comments support is not implemented in the new DLL version yet. + Now CmtState is always 0. + +/* + * Input parameter which should point to the buffer for file + * comments. Maximum comment size is limited to 64Kb. Comment text is + * a zero terminated string in OEM encoding. If the comment text is + * larger than the buffer size, the comment text will be truncated. + * If CmtBuf is set to NULL, comments will not be read. + */ + +CmtBufSize + Input parameter which should contain size of buffer for archive + comments. + +CmtSize + Output parameter containing size of comments actually read into the + buffer, should not exceed CmtBufSize. + +CmtState + Output parameter. + + Possible values + + 0 Absent comments + 1 Comments read completely + ERAR_NO_MEMORY Not enough memory to extract comments + ERAR_BAD_DATA Broken comment + ERAR_UNKNOWN_FORMAT Unknown comment format + ERAR_SMALL_BUF Buffer too small, comments not completely read + +Return values +~~~~~~~~~~~~~ + + 0 Success + ERAR_END_ARCHIVE End of archive + ERAR_BAD_DATA File header broken + + +==================================================================== +int PASCAL RARReadHeaderEx(HANDLE hArcData, + struct RARHeaderDataEx *HeaderData) +==================================================================== + +Description +~~~~~~~~~~~ + Similar to RARReadHeader, but uses RARHeaderDataEx structure, +containing information about Unicode file names and 64 bit file sizes. + +struct RARHeaderDataEx +{ + char ArcName[1024]; + wchar_t ArcNameW[1024]; + char FileName[1024]; + wchar_t FileNameW[1024]; + unsigned int Flags; + unsigned int PackSize; + unsigned int PackSizeHigh; + unsigned int UnpSize; + unsigned int UnpSizeHigh; + unsigned int HostOS; + unsigned int FileCRC; + unsigned int FileTime; + unsigned int UnpVer; + unsigned int Method; + unsigned int FileAttr; + char *CmtBuf; + unsigned int CmtBufSize; + unsigned int CmtSize; + unsigned int CmtState; + unsigned int Reserved[1024]; +}; + + +==================================================================== +int PASCAL RARProcessFile(HANDLE hArcData, + int Operation, + char *DestPath, + char *DestName) +==================================================================== + +Description +~~~~~~~~~~~ + Performs action and moves the current position in the archive to + the next file. Extract or test the current file from the archive + opened in RAR_OM_EXTRACT mode. If the mode RAR_OM_LIST is set, + then a call to this function will simply skip the archive position + to the next file. + +Parameters +~~~~~~~~~~ +hArcData + This parameter should contain the archive handle obtained from the + RAROpenArchive function call. + +Operation + File operation. + + Possible values + + RAR_SKIP Move to the next file in the archive. If the + archive is solid and RAR_OM_EXTRACT mode was set + when the archive was opened, the current file will + be processed - the operation will be performed + slower than a simple seek. + + RAR_TEST Test the current file and move to the next file in + the archive. If the archive was opened with + RAR_OM_LIST mode, the operation is equal to + RAR_SKIP. + + RAR_EXTRACT Extract the current file and move to the next file. + If the archive was opened with RAR_OM_LIST mode, + the operation is equal to RAR_SKIP. + + +DestPath + This parameter should point to a zero terminated string containing the + destination directory to which to extract files to. If DestPath is equal + to NULL, it means extract to the current directory. This parameter has + meaning only if DestName is NULL. + +DestName + This parameter should point to a string containing the full path and name + to assign to extracted file or it can be NULL to use the default name. + If DestName is defined (not NULL), it overrides both the original file + name saved in the archive and path specigied in DestPath setting. + + Both DestPath and DestName must be in OEM encoding. If necessary, + use CharToOem to convert text to OEM before passing to this function. + +Return values +~~~~~~~~~~~~~ + 0 Success + ERAR_BAD_DATA File CRC error + ERAR_BAD_ARCHIVE Volume is not valid RAR archive + ERAR_UNKNOWN_FORMAT Unknown archive format + ERAR_EOPEN Volume open error + ERAR_ECREATE File create error + ERAR_ECLOSE File close error + ERAR_EREAD Read error + ERAR_EWRITE Write error + + +Note: if you wish to cancel extraction, return -1 when processing + UCM_PROCESSDATA callback message. + + +==================================================================== +int PASCAL RARProcessFileW(HANDLE hArcData, + int Operation, + wchar_t *DestPath, + wchar_t *DestName) +==================================================================== + +Description +~~~~~~~~~~~ + Unicode version of RARProcessFile. It uses Unicode DestPath + and DestName parameters, other parameters and return values + are the same as in RARProcessFile. + + +==================================================================== +void PASCAL RARSetCallback(HANDLE hArcData, + int PASCAL (*CallbackProc)(UINT msg,LPARAM UserData,LPARAM P1,LPARAM P2), + LPARAM UserData); +==================================================================== + +Description +~~~~~~~~~~~ + Set a user-defined callback function to process Unrar events. + +Parameters +~~~~~~~~~~ +hArcData + This parameter should contain the archive handle obtained from the + RAROpenArchive function call. + +CallbackProc + It should point to a user-defined callback function. + + The function will be passed four parameters: + + + msg Type of event. Described below. + + UserData User defined value passed to RARSetCallback. + + P1 and P2 Event dependent parameters. Described below. + + + Possible events + + UCM_CHANGEVOLUME Process volume change. + + P1 Points to the zero terminated name + of the next volume. + + P2 The function call mode: + + RAR_VOL_ASK Required volume is absent. The function should + prompt user and return a positive value + to retry or return -1 value to terminate + operation. The function may also specify a new + volume name, placing it to the address specified + by P1 parameter. + + RAR_VOL_NOTIFY Required volume is successfully opened. + This is a notification call and volume name + modification is not allowed. The function should + return a positive value to continue or -1 + to terminate operation. + + UCM_PROCESSDATA Process unpacked data. It may be used to read + a file while it is being extracted or tested + without actual extracting file to disk. + Return a positive value to continue process + or -1 to cancel the archive operation + + P1 Address pointing to the unpacked data. + Function may refer to the data but must not + change it. + + P2 Size of the unpacked data. It is guaranteed + only that the size will not exceed the maximum + dictionary size (4 Mb in RAR 3.0). + + UCM_NEEDPASSWORD DLL needs a password to process archive. + This message must be processed if you wish + to be able to handle archives with encrypted + file names. It can be also used as replacement + of RARSetPassword function even for usual + encrypted files with non-encrypted names. + + P1 Address pointing to the buffer for a password. + You need to copy a password here. + + P2 Size of the password buffer. + + +UserData + User data passed to callback function. + + Other functions of UnRAR.dll should not be called from the callback + function. + +Return values +~~~~~~~~~~~~~ + None + + + +==================================================================== +void PASCAL RARSetChangeVolProc(HANDLE hArcData, + int PASCAL (*ChangeVolProc)(char *ArcName,int Mode)); +==================================================================== + +Obsoleted, use RARSetCallback instead. + + + +==================================================================== +void PASCAL RARSetProcessDataProc(HANDLE hArcData, + int PASCAL (*ProcessDataProc)(unsigned char *Addr,int Size)) +==================================================================== + +Obsoleted, use RARSetCallback instead. + + +==================================================================== +void PASCAL RARSetPassword(HANDLE hArcData, + char *Password); +==================================================================== + +Description +~~~~~~~~~~~ + Set a password to decrypt files. + +Parameters +~~~~~~~~~~ +hArcData + This parameter should contain the archive handle obtained from the + RAROpenArchive function call. + +Password + It should point to a string containing a zero terminated password. + +Return values +~~~~~~~~~~~~~ + None + + +==================================================================== +void PASCAL RARGetDllVersion(); +==================================================================== + +Description +~~~~~~~~~~~ + Returns API version. + +Parameters +~~~~~~~~~~ + None. + +Return values +~~~~~~~~~~~~~ + Returns an integer value denoting UnRAR.dll API version, which is also +defined in unrar.h as RAR_DLL_VERSION. API version number is incremented +only in case of noticeable changes in UnRAR.dll API. Do not confuse it +with version of UnRAR.dll stored in DLL resources, which is incremented +with every DLL rebuild. + + If RARGetDllVersion() returns a value lower than UnRAR.dll which your +application was designed for, it may indicate that DLL version is too old +and it will fail to provide all necessary functions to your application. + + This function is absent in old versions of UnRAR.dll, so it is safer +to use LoadLibrary and GetProcAddress to access this function. + diff --git a/UnRAR2/UnRARDLL/whatsnew.txt b/UnRAR2/UnRARDLL/whatsnew.txt new file mode 100644 index 0000000..84ad72c --- /dev/null +++ b/UnRAR2/UnRARDLL/whatsnew.txt @@ -0,0 +1,80 @@ +List of unrar.dll API changes. We do not include performance and reliability +improvements into this list, but this library and RAR/UnRAR tools share +the same source code. So the latest version of unrar.dll usually contains +same decompression algorithm changes as the latest UnRAR version. +============================================================================ + +-- 18 January 2008 + +all LONG parameters of CallbackProc function were changed +to LPARAM type for 64 bit mode compatibility. + + +-- 12 December 2007 + +Added new RAR_OM_LIST_INCSPLIT open mode for function RAROpenArchive. + + +-- 14 August 2007 + +Added NoCrypt\unrar_nocrypt.dll without decryption code for those +applications where presence of encryption or decryption code is not +allowed because of legal restrictions. + + +-- 14 December 2006 + +Added ERAR_MISSING_PASSWORD error type. This error is returned +if empty password is specified for encrypted file. + + +-- 12 June 2003 + +Added RARProcessFileW function, Unicode version of RARProcessFile + + +-- 9 August 2002 + +Added RAROpenArchiveEx function allowing to specify Unicode archive +name and get archive flags. + + +-- 24 January 2002 + +Added RARReadHeaderEx function allowing to read Unicode file names +and 64 bit file sizes. + + +-- 23 January 2002 + +Added ERAR_UNKNOWN error type (it is used for all errors which +do not have special ERAR code yet) and UCM_NEEDPASSWORD callback +message. + +Unrar.dll automatically opens all next volumes not only when extracting, +but also in RAR_OM_LIST mode. + + +-- 27 November 2001 + +RARSetChangeVolProc and RARSetProcessDataProc are replaced by +the single callback function installed with RARSetCallback. +Unlike old style callbacks, the new function accepts the user defined +parameter. Unrar.dll still supports RARSetChangeVolProc and +RARSetProcessDataProc for compatibility purposes, but if you write +a new application, better use RARSetCallback. + +File comments support is not implemented in the new DLL version yet. +Now CmtState is always 0. + + +-- 13 August 2001 + +Added RARGetDllVersion function, so you may distinguish old unrar.dll, +which used C style callback functions and the new one with PASCAL callbacks. + + +-- 10 May 2001 + +Callback functions in RARSetChangeVolProc and RARSetProcessDataProc +use PASCAL style call convention now. diff --git a/UnRAR2/UnRARDLL/x64/readme.txt b/UnRAR2/UnRARDLL/x64/readme.txt new file mode 100644 index 0000000..8f3b4e1 --- /dev/null +++ b/UnRAR2/UnRARDLL/x64/readme.txt @@ -0,0 +1 @@ +This is x64 version of unrar.dll. diff --git a/UnRAR2/UnRARDLL/x64/unrar64.dll b/UnRAR2/UnRARDLL/x64/unrar64.dll new file mode 100644 index 0000000000000000000000000000000000000000..e17a19e59113c2bd17d1db85f594a1f7c8d4707f GIT binary patch literal 191488 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 literal 0 HcmV?d00001 diff --git a/UnRAR2/UnRARDLL/x64/unrar64.lib b/UnRAR2/UnRARDLL/x64/unrar64.lib new file mode 100644 index 0000000000000000000000000000000000000000..fd037919ee10c91665fa5f30e9ec6f37ac9e03c7 GIT binary patch literal 3972 zcmcImJ5L)y5FTt^e(;c!Dj|{+Ath-DG3N`GqR0l5VDYldtHT_=3$}9D$TkUGIz*8! zb;?lEr=v!Sl){l{Qsh6-lUeWH-K@_T8+R=JFX0GJG)aPil0o?14WT1u~E*0RaD)!9rcy|$9c9u&)3SydpLU0z>YbQ{2D zVZq1O=GT`OvdP3+0y2rr%Dz!dU>FJ?4V<8`x1ViL%H6rcrdFs%yIHeTFtp!L@t7EYzHkb;~448R}+APB=S1U=xncFgNykIP$BP^!vZHz9Cb1ex(;lg;n&NAB^TZ`y9008Qv_ zur8+{+2dPer=4|DqS#t_cWpHdMl&VN91;nKHAG2Lg9OQ*)zQ&OX~1k#rj&Alj@ZGI zlno)g_N)?T*o1DHb&B6~N-MD``;^v?ypXDWTL|<3GLn}G-Pk$U9bXk)NMPfO)e&_Z zMrS4pI;QY=#2UJfjh`rgg~E$<9460%9iHC2V2Z#Mie<;NaR{Gzunu8%uo56nJ%xLt zn0D~1K7w}%*1p5gQXy9^RZ8!x;b)oW;l@3K>vR4ml53`RbY<7T{=&{NJ z8T{VurRFE@w#Af4)VkTX{rZuo&XgbZ#jzyD7qIBL_XE5H6A0N>>1QEQQ3Q7cd^KUsd!5{I~zUy7w?cE7ETsF~fJN4MR` z-rdP%UC3efzGO+NAFDqoRO#VZ@&!b}I?go3eo@Pw zv3G-qqtSb&xN%qfe|O$=y&Y@eo8QK> Y-(qC?Tll6l_w5P#7I3f{o_G}UA258WCIA2c literal 0 HcmV?d00001 diff --git a/UnRAR2/__init__.py b/UnRAR2/__init__.py new file mode 100644 index 0000000..a913fcb --- /dev/null +++ b/UnRAR2/__init__.py @@ -0,0 +1,177 @@ +# Copyright (c) 2003-2005 Jimmy Retzlaff, 2008 Konstantin Yegupov +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +""" +pyUnRAR2 is a ctypes based wrapper around the free UnRAR.dll. + +It is an modified version of Jimmy Retzlaff's pyUnRAR - more simple, +stable and foolproof. +Notice that it has INCOMPATIBLE interface. + +It enables reading and unpacking of archives created with the +RAR/WinRAR archivers. There is a low-level interface which is very +similar to the C interface provided by UnRAR. There is also a +higher level interface which makes some common operations easier. +""" + +__version__ = '0.99.3' + +try: + WindowsError + in_windows = True +except NameError: + in_windows = False + +if in_windows: + from windows import RarFileImplementation +else: + from unix import RarFileImplementation + + +import fnmatch, time, weakref + +class RarInfo(object): + """Represents a file header in an archive. Don't instantiate directly. + Use only to obtain information about file. + YOU CANNOT EXTRACT FILE CONTENTS USING THIS OBJECT. + USE METHODS OF RarFile CLASS INSTEAD. + + Properties: + index - index of file within the archive + filename - name of the file in the archive including path (if any) + datetime - file date/time as a struct_time suitable for time.strftime + isdir - True if the file is a directory + size - size in bytes of the uncompressed file + comment - comment associated with the file + + Note - this is not currently intended to be a Python file-like object. + """ + + def __init__(self, rarfile, data): + self.rarfile = weakref.proxy(rarfile) + self.index = data['index'] + self.filename = data['filename'] + self.isdir = data['isdir'] + self.size = data['size'] + self.datetime = data['datetime'] + self.comment = data['comment'] + + + + def __str__(self): + try : + arcName = self.rarfile.archiveName + except ReferenceError: + arcName = "[ARCHIVE_NO_LONGER_LOADED]" + return '' % (self.filename, arcName) + +class RarFile(RarFileImplementation): + + def __init__(self, archiveName, password=None): + """Instantiate the archive. + + archiveName is the name of the RAR file. + password is used to decrypt the files in the archive. + + Properties: + comment - comment associated with the archive + + >>> print RarFile('test.rar').comment + This is a test. + """ + self.archiveName = archiveName + RarFileImplementation.init(self, password) + + def __del__(self): + self.destruct() + + def infoiter(self): + """Iterate over all the files in the archive, generating RarInfos. + + >>> import os + >>> for fileInArchive in RarFile('test.rar').infoiter(): + ... print os.path.split(fileInArchive.filename)[-1], + ... print fileInArchive.isdir, + ... print fileInArchive.size, + ... print fileInArchive.comment, + ... print tuple(fileInArchive.datetime)[0:5], + ... print time.strftime('%a, %d %b %Y %H:%M', fileInArchive.datetime) + test True 0 None (2003, 6, 30, 1, 59) Mon, 30 Jun 2003 01:59 + test.txt False 20 None (2003, 6, 30, 2, 1) Mon, 30 Jun 2003 02:01 + this.py False 1030 None (2002, 2, 8, 16, 47) Fri, 08 Feb 2002 16:47 + """ + for params in RarFileImplementation.infoiter(self): + yield RarInfo(self, params) + + def infolist(self): + """Return a list of RarInfos, descripting the contents of the archive.""" + return list(self.infoiter()) + + def read_files(self, condition='*'): + """Read specific files from archive into memory. + If "condition" is a list of numbers, then return files which have those positions in infolist. + If "condition" is a string, then it is treated as a wildcard for names of files to extract. + If "condition" is a function, it is treated as a callback function, which accepts a RarInfo object + and returns boolean True (extract) or False (skip). + If "condition" is omitted, all files are returned. + + Returns list of tuples (RarInfo info, str contents) + """ + checker = condition2checker(condition) + return RarFileImplementation.read_files(self, checker) + + + def extract(self, condition='*', path='.', withSubpath=True, overwrite=True): + """Extract specific files from archive to disk. + + If "condition" is a list of numbers, then extract files which have those positions in infolist. + If "condition" is a string, then it is treated as a wildcard for names of files to extract. + If "condition" is a function, it is treated as a callback function, which accepts a RarInfo object + and returns either boolean True (extract) or boolean False (skip). + DEPRECATED: If "condition" callback returns string (only supported for Windows) - + that string will be used as a new name to save the file under. + If "condition" is omitted, all files are extracted. + + "path" is a directory to extract to + "withSubpath" flag denotes whether files are extracted with their full path in the archive. + "overwrite" flag denotes whether extracted files will overwrite old ones. Defaults to true. + + Returns list of RarInfos for extracted files.""" + checker = condition2checker(condition) + return RarFileImplementation.extract(self, checker, path, withSubpath, overwrite) + +def condition2checker(condition): + """Converts different condition types to callback""" + if type(condition) in [str, unicode]: + def smatcher(info): + return fnmatch.fnmatch(info.filename, condition) + return smatcher + elif type(condition) in [list, tuple] and type(condition[0]) in [int, long]: + def imatcher(info): + return info.index in condition + return imatcher + elif callable(condition): + return condition + else: + raise TypeError + + diff --git a/UnRAR2/rar_exceptions.py b/UnRAR2/rar_exceptions.py new file mode 100644 index 0000000..d90d1c8 --- /dev/null +++ b/UnRAR2/rar_exceptions.py @@ -0,0 +1,30 @@ +# Copyright (c) 2003-2005 Jimmy Retzlaff, 2008 Konstantin Yegupov +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +# Low level interface - see UnRARDLL\UNRARDLL.TXT + + +class ArchiveHeaderBroken(Exception): pass +class InvalidRARArchive(Exception): pass +class FileOpenError(Exception): pass +class IncorrectRARPassword(Exception): pass +class InvalidRARArchiveUsage(Exception): pass diff --git a/UnRAR2/test_UnRAR2.py b/UnRAR2/test_UnRAR2.py new file mode 100644 index 0000000..e86ba2c --- /dev/null +++ b/UnRAR2/test_UnRAR2.py @@ -0,0 +1,138 @@ +import os, sys + +import UnRAR2 +from UnRAR2.rar_exceptions import * + + +def cleanup(dir='test'): + for path, dirs, files in os.walk(dir): + for fn in files: + os.remove(os.path.join(path, fn)) + for dir in dirs: + os.removedirs(os.path.join(path, dir)) + + +# basic test +cleanup() +rarc = UnRAR2.RarFile('test.rar') +rarc.infolist() +assert rarc.comment == "This is a test." +for info in rarc.infoiter(): + saveinfo = info + assert (str(info)=="""""") + break +rarc.extract() +assert os.path.exists('test'+os.sep+'test.txt') +assert os.path.exists('test'+os.sep+'this.py') +del rarc +assert (str(saveinfo)=="""""") +cleanup() + +# extract all the files in test.rar +cleanup() +UnRAR2.RarFile('test.rar').extract() +assert os.path.exists('test'+os.sep+'test.txt') +assert os.path.exists('test'+os.sep+'this.py') +cleanup() + +# extract all the files in test.rar matching the wildcard *.txt +cleanup() +UnRAR2.RarFile('test.rar').extract('*.txt') +assert os.path.exists('test'+os.sep+'test.txt') +assert not os.path.exists('test'+os.sep+'this.py') +cleanup() + + +# check the name and size of each file, extracting small ones +cleanup() +archive = UnRAR2.RarFile('test.rar') +assert archive.comment == 'This is a test.' +archive.extract(lambda rarinfo: rarinfo.size <= 1024) +for rarinfo in archive.infoiter(): + if rarinfo.size <= 1024 and not rarinfo.isdir: + assert rarinfo.size == os.stat(rarinfo.filename).st_size +assert file('test'+os.sep+'test.txt', 'rt').read() == 'This is only a test.' +assert not os.path.exists('test'+os.sep+'this.py') +cleanup() + + +# extract this.py, overriding it's destination +cleanup('test2') +archive = UnRAR2.RarFile('test.rar') +archive.extract('*.py', 'test2', False) +assert os.path.exists('test2'+os.sep+'this.py') +cleanup('test2') + + +# extract test.txt to memory +cleanup() +archive = UnRAR2.RarFile('test.rar') +entries = UnRAR2.RarFile('test.rar').read_files('*test.txt') +assert len(entries)==1 +assert entries[0][0].filename.endswith('test.txt') +assert entries[0][1]=='This is only a test.' + + +# extract all the files in test.rar with overwriting +cleanup() +fo = open('test'+os.sep+'test.txt',"wt") +fo.write("blah") +fo.close() +UnRAR2.RarFile('test.rar').extract('*.txt') +assert open('test'+os.sep+'test.txt',"rt").read()!="blah" +cleanup() + +# extract all the files in test.rar without overwriting +cleanup() +fo = open('test'+os.sep+'test.txt',"wt") +fo.write("blahblah") +fo.close() +UnRAR2.RarFile('test.rar').extract('*.txt', overwrite = False) +assert open('test'+os.sep+'test.txt',"rt").read()=="blahblah" +cleanup() + +# list big file in an archive +list(UnRAR2.RarFile('test_nulls.rar').infoiter()) + +# extract files from an archive with protected files +cleanup() +rarc = UnRAR2.RarFile('test_protected_files.rar', password="protected") +rarc.extract() +assert os.path.exists('test'+os.sep+'top_secret_xxx_file.txt') +cleanup() +errored = False +try: + UnRAR2.RarFile('test_protected_files.rar', password="proteqted").extract() +except IncorrectRARPassword: + errored = True +assert not os.path.exists('test'+os.sep+'top_secret_xxx_file.txt') +assert errored +cleanup() + +# extract files from an archive with protected headers +cleanup() +UnRAR2.RarFile('test_protected_headers.rar', password="secret").extract() +assert os.path.exists('test'+os.sep+'top_secret_xxx_file.txt') +cleanup() +errored = False +try: + UnRAR2.RarFile('test_protected_headers.rar', password="seqret").extract() +except IncorrectRARPassword: + errored = True +assert not os.path.exists('test'+os.sep+'top_secret_xxx_file.txt') +assert errored +cleanup() + +# make sure docstring examples are working +import doctest +doctest.testmod(UnRAR2) + +# update documentation +import pydoc +pydoc.writedoc(UnRAR2) + +# cleanup +try: + os.remove('__init__.pyc') +except: + pass diff --git a/UnRAR2/unix.py b/UnRAR2/unix.py new file mode 100644 index 0000000..bd9ee85 --- /dev/null +++ b/UnRAR2/unix.py @@ -0,0 +1,218 @@ +# Copyright (c) 2003-2005 Jimmy Retzlaff, 2008 Konstantin Yegupov +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +# Unix version uses unrar command line executable + +import subprocess +import gc + +import os, os.path +import time, re + +from rar_exceptions import * + +class UnpackerNotInstalled(Exception): pass + +rar_executable_cached = None +rar_executable_version = None + +def call_unrar(params): + "Calls rar/unrar command line executable, returns stdout pipe" + global rar_executable_cached + if rar_executable_cached is None: + for command in ('unrar', 'rar'): + try: + subprocess.Popen([command], stdout=subprocess.PIPE) + rar_executable_cached = command + break + except OSError: + pass + if rar_executable_cached is None: + raise UnpackerNotInstalled("No suitable RAR unpacker installed") + + assert type(params) == list, "params must be list" + args = [rar_executable_cached] + params + try: + gc.disable() # See http://bugs.python.org/issue1336 + return subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + finally: + gc.enable() + +class RarFileImplementation(object): + + def init(self, password=None): + global rar_executable_version + self.password = password + + + stdoutdata, stderrdata = self.call('v', []).communicate() + + for line in stderrdata.splitlines(): + if line.strip().startswith("Cannot open"): + raise FileOpenError + if line.find("CRC failed")>=0: + raise IncorrectRARPassword + accum = [] + source = iter(stdoutdata.splitlines()) + line = '' + while not (line.startswith('UNRAR')): + line = source.next() + signature = line + # The code below is mighty flaky + # and will probably crash on localized versions of RAR + # but I see no safe way to rewrite it using a CLI tool + if signature.startswith("UNRAR 4"): + rar_executable_version = 4 + while not (line.startswith('Comment:') or line.startswith('Pathname/Comment')): + if line.strip().endswith('is not RAR archive'): + raise InvalidRARArchive + line = source.next() + while not line.startswith('Pathname/Comment'): + accum.append(line.rstrip('\n')) + line = source.next() + if len(accum): + accum[0] = accum[0][9:] # strip out "Comment:" part + self.comment = '\n'.join(accum[:-1]) + else: + self.comment = None + elif signature.startswith("UNRAR 5"): + rar_executable_version = 5 + line = source.next() + while not line.startswith('Archive:'): + if line.strip().endswith('is not RAR archive'): + raise InvalidRARArchive + accum.append(line.rstrip('\n')) + line = source.next() + if len(accum): + self.comment = '\n'.join(accum[:-1]).strip() + else: + self.comment = None + else: + raise UnpackerNotInstalled("Unsupported RAR version, expected 4.x or 5.x, found: " + + signature.split(" ")[1]) + + + def escaped_password(self): + return '-' if self.password == None else self.password + + + def call(self, cmd, options=[], files=[]): + options2 = options + ['p'+self.escaped_password()] + soptions = ['-'+x for x in options2] + return call_unrar([cmd]+soptions+['--',self.archiveName]+files) + + def infoiter(self): + + command = "v" if rar_executable_version == 4 else "l" + stdoutdata, stderrdata = self.call(command, ['c-']).communicate() + + for line in stderrdata.splitlines(): + if line.strip().startswith("Cannot open"): + raise FileOpenError + + accum = [] + source = iter(stdoutdata.splitlines()) + line = '' + while not line.startswith('-----------'): + if line.strip().endswith('is not RAR archive'): + raise InvalidRARArchive + if line.startswith("CRC failed") or line.startswith("Checksum error"): + raise IncorrectRARPassword + line = source.next() + line = source.next() + i = 0 + re_spaces = re.compile(r"\s+") + if rar_executable_version == 4: + while not line.startswith('-----------'): + accum.append(line) + if len(accum)==2: + data = {} + data['index'] = i + # asterisks mark password-encrypted files + data['filename'] = accum[0].strip().lstrip("*") # asterisks marks password-encrypted files + fields = re_spaces.split(accum[1].strip()) + data['size'] = int(fields[0]) + attr = fields[5] + data['isdir'] = 'd' in attr.lower() + data['datetime'] = time.strptime(fields[3]+" "+fields[4], '%d-%m-%y %H:%M') + data['comment'] = None + yield data + accum = [] + i += 1 + line = source.next() + elif rar_executable_version == 5: + while not line.startswith('-----------'): + fields = line.strip().lstrip("*").split() + data = {} + data['index'] = i + data['filename'] = " ".join(fields[4:]) + data['size'] = int(fields[1]) + attr = fields[0] + data['isdir'] = 'd' in attr.lower() + data['datetime'] = time.strptime(fields[2]+" "+fields[3], '%d-%m-%y %H:%M') + data['comment'] = None + yield data + i += 1 + line = source.next() + + + def read_files(self, checker): + res = [] + for info in self.infoiter(): + checkres = checker(info) + if checkres==True and not info.isdir: + pipe = self.call('p', ['inul'], [info.filename]).stdout + res.append((info, pipe.read())) + return res + + + def extract(self, checker, path, withSubpath, overwrite): + res = [] + command = 'x' + if not withSubpath: + command = 'e' + options = [] + if overwrite: + options.append('o+') + else: + options.append('o-') + if not path.endswith(os.sep): + path += os.sep + names = [] + for info in self.infoiter(): + checkres = checker(info) + if type(checkres) in [str, unicode]: + raise NotImplementedError("Condition callbacks returning strings are deprecated and only supported in Windows") + if checkres==True and not info.isdir: + names.append(info.filename) + res.append(info) + names.append(path) + proc = self.call(command, options, names) + stdoutdata, stderrdata = proc.communicate() + if stderrdata.find("CRC failed")>=0 or stderrdata.find("Checksum error")>=0: + raise IncorrectRARPassword + return res + + def destruct(self): + pass + + diff --git a/UnRAR2/windows.py b/UnRAR2/windows.py new file mode 100644 index 0000000..bb92481 --- /dev/null +++ b/UnRAR2/windows.py @@ -0,0 +1,309 @@ +# Copyright (c) 2003-2005 Jimmy Retzlaff, 2008 Konstantin Yegupov +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +# Low level interface - see UnRARDLL\UNRARDLL.TXT + +from __future__ import generators + +import ctypes, ctypes.wintypes +import os, os.path, sys +import Queue +import time + +from rar_exceptions import * + +ERAR_END_ARCHIVE = 10 +ERAR_NO_MEMORY = 11 +ERAR_BAD_DATA = 12 +ERAR_BAD_ARCHIVE = 13 +ERAR_UNKNOWN_FORMAT = 14 +ERAR_EOPEN = 15 +ERAR_ECREATE = 16 +ERAR_ECLOSE = 17 +ERAR_EREAD = 18 +ERAR_EWRITE = 19 +ERAR_SMALL_BUF = 20 +ERAR_UNKNOWN = 21 + +RAR_OM_LIST = 0 +RAR_OM_EXTRACT = 1 + +RAR_SKIP = 0 +RAR_TEST = 1 +RAR_EXTRACT = 2 + +RAR_VOL_ASK = 0 +RAR_VOL_NOTIFY = 1 + +RAR_DLL_VERSION = 3 + +# enum UNRARCALLBACK_MESSAGES +UCM_CHANGEVOLUME = 0 +UCM_PROCESSDATA = 1 +UCM_NEEDPASSWORD = 2 + +architecture_bits = ctypes.sizeof(ctypes.c_voidp)*8 +dll_name = "unrar.dll" +if architecture_bits == 64: + dll_name = "x64\\unrar64.dll" + + +try: + unrar = ctypes.WinDLL(os.path.join(os.path.split(__file__)[0], 'UnRARDLL', dll_name)) +except WindowsError: + unrar = ctypes.WinDLL(dll_name) + + +class RAROpenArchiveDataEx(ctypes.Structure): + def __init__(self, ArcName=None, ArcNameW=u'', OpenMode=RAR_OM_LIST): + self.CmtBuf = ctypes.c_buffer(64*1024) + ctypes.Structure.__init__(self, ArcName=ArcName, ArcNameW=ArcNameW, OpenMode=OpenMode, _CmtBuf=ctypes.addressof(self.CmtBuf), CmtBufSize=ctypes.sizeof(self.CmtBuf)) + + _fields_ = [ + ('ArcName', ctypes.c_char_p), + ('ArcNameW', ctypes.c_wchar_p), + ('OpenMode', ctypes.c_uint), + ('OpenResult', ctypes.c_uint), + ('_CmtBuf', ctypes.c_voidp), + ('CmtBufSize', ctypes.c_uint), + ('CmtSize', ctypes.c_uint), + ('CmtState', ctypes.c_uint), + ('Flags', ctypes.c_uint), + ('Reserved', ctypes.c_uint*32), + ] + +class RARHeaderDataEx(ctypes.Structure): + def __init__(self): + self.CmtBuf = ctypes.c_buffer(64*1024) + ctypes.Structure.__init__(self, _CmtBuf=ctypes.addressof(self.CmtBuf), CmtBufSize=ctypes.sizeof(self.CmtBuf)) + + _fields_ = [ + ('ArcName', ctypes.c_char*1024), + ('ArcNameW', ctypes.c_wchar*1024), + ('FileName', ctypes.c_char*1024), + ('FileNameW', ctypes.c_wchar*1024), + ('Flags', ctypes.c_uint), + ('PackSize', ctypes.c_uint), + ('PackSizeHigh', ctypes.c_uint), + ('UnpSize', ctypes.c_uint), + ('UnpSizeHigh', ctypes.c_uint), + ('HostOS', ctypes.c_uint), + ('FileCRC', ctypes.c_uint), + ('FileTime', ctypes.c_uint), + ('UnpVer', ctypes.c_uint), + ('Method', ctypes.c_uint), + ('FileAttr', ctypes.c_uint), + ('_CmtBuf', ctypes.c_voidp), + ('CmtBufSize', ctypes.c_uint), + ('CmtSize', ctypes.c_uint), + ('CmtState', ctypes.c_uint), + ('Reserved', ctypes.c_uint*1024), + ] + +def DosDateTimeToTimeTuple(dosDateTime): + """Convert an MS-DOS format date time to a Python time tuple. + """ + dosDate = dosDateTime >> 16 + dosTime = dosDateTime & 0xffff + day = dosDate & 0x1f + month = (dosDate >> 5) & 0xf + year = 1980 + (dosDate >> 9) + second = 2*(dosTime & 0x1f) + minute = (dosTime >> 5) & 0x3f + hour = dosTime >> 11 + return time.localtime(time.mktime((year, month, day, hour, minute, second, 0, 1, -1))) + +def _wrap(restype, function, argtypes): + result = function + result.argtypes = argtypes + result.restype = restype + return result + +RARGetDllVersion = _wrap(ctypes.c_int, unrar.RARGetDllVersion, []) + +RAROpenArchiveEx = _wrap(ctypes.wintypes.HANDLE, unrar.RAROpenArchiveEx, [ctypes.POINTER(RAROpenArchiveDataEx)]) + +RARReadHeaderEx = _wrap(ctypes.c_int, unrar.RARReadHeaderEx, [ctypes.wintypes.HANDLE, ctypes.POINTER(RARHeaderDataEx)]) + +_RARSetPassword = _wrap(ctypes.c_int, unrar.RARSetPassword, [ctypes.wintypes.HANDLE, ctypes.c_char_p]) +def RARSetPassword(*args, **kwargs): + _RARSetPassword(*args, **kwargs) + +RARProcessFile = _wrap(ctypes.c_int, unrar.RARProcessFile, [ctypes.wintypes.HANDLE, ctypes.c_int, ctypes.c_char_p, ctypes.c_char_p]) + +RARCloseArchive = _wrap(ctypes.c_int, unrar.RARCloseArchive, [ctypes.wintypes.HANDLE]) + +UNRARCALLBACK = ctypes.WINFUNCTYPE(ctypes.c_int, ctypes.c_uint, ctypes.c_long, ctypes.c_long, ctypes.c_long) +RARSetCallback = _wrap(ctypes.c_int, unrar.RARSetCallback, [ctypes.wintypes.HANDLE, UNRARCALLBACK, ctypes.c_long]) + + + +RARExceptions = { + ERAR_NO_MEMORY : MemoryError, + ERAR_BAD_DATA : ArchiveHeaderBroken, + ERAR_BAD_ARCHIVE : InvalidRARArchive, + ERAR_EOPEN : FileOpenError, + } + +class PassiveReader: + """Used for reading files to memory""" + def __init__(self, usercallback = None): + self.buf = [] + self.ucb = usercallback + + def _callback(self, msg, UserData, P1, P2): + if msg == UCM_PROCESSDATA: + data = (ctypes.c_char*P2).from_address(P1).raw + if self.ucb!=None: + self.ucb(data) + else: + self.buf.append(data) + return 1 + + def get_result(self): + return ''.join(self.buf) + +class RarInfoIterator(object): + def __init__(self, arc): + self.arc = arc + self.index = 0 + self.headerData = RARHeaderDataEx() + self.res = RARReadHeaderEx(self.arc._handle, ctypes.byref(self.headerData)) + if self.res==ERAR_BAD_DATA: + raise IncorrectRARPassword + self.arc.lockStatus = "locked" + self.arc.needskip = False + + def __iter__(self): + return self + + def next(self): + if self.index>0: + if self.arc.needskip: + RARProcessFile(self.arc._handle, RAR_SKIP, None, None) + self.res = RARReadHeaderEx(self.arc._handle, ctypes.byref(self.headerData)) + + if self.res: + raise StopIteration + self.arc.needskip = True + + data = {} + data['index'] = self.index + data['filename'] = self.headerData.FileName + data['datetime'] = DosDateTimeToTimeTuple(self.headerData.FileTime) + data['isdir'] = ((self.headerData.Flags & 0xE0) == 0xE0) + data['size'] = self.headerData.UnpSize + (self.headerData.UnpSizeHigh << 32) + if self.headerData.CmtState == 1: + data['comment'] = self.headerData.CmtBuf.value + else: + data['comment'] = None + self.index += 1 + return data + + + def __del__(self): + self.arc.lockStatus = "finished" + +def generate_password_provider(password): + def password_provider_callback(msg, UserData, P1, P2): + if msg == UCM_NEEDPASSWORD and password!=None: + (ctypes.c_char*P2).from_address(P1).value = password + return 1 + return password_provider_callback + +class RarFileImplementation(object): + + def init(self, password=None): + self.password = password + archiveData = RAROpenArchiveDataEx(ArcNameW=self.archiveName, OpenMode=RAR_OM_EXTRACT) + self._handle = RAROpenArchiveEx(ctypes.byref(archiveData)) + self.c_callback = UNRARCALLBACK(generate_password_provider(self.password)) + RARSetCallback(self._handle, self.c_callback, 1) + + if archiveData.OpenResult != 0: + raise RARExceptions[archiveData.OpenResult] + + if archiveData.CmtState == 1: + self.comment = archiveData.CmtBuf.value + else: + self.comment = None + + if password: + RARSetPassword(self._handle, password) + + self.lockStatus = "ready" + + + + def destruct(self): + if self._handle and RARCloseArchive: + RARCloseArchive(self._handle) + + def make_sure_ready(self): + if self.lockStatus == "locked": + raise InvalidRARArchiveUsage("cannot execute infoiter() without finishing previous one") + if self.lockStatus == "finished": + self.destruct() + self.init(self.password) + + def infoiter(self): + self.make_sure_ready() + return RarInfoIterator(self) + + def read_files(self, checker): + res = [] + for info in self.infoiter(): + if checker(info) and not info.isdir: + reader = PassiveReader() + c_callback = UNRARCALLBACK(reader._callback) + RARSetCallback(self._handle, c_callback, 1) + tmpres = RARProcessFile(self._handle, RAR_TEST, None, None) + if tmpres==ERAR_BAD_DATA: + raise IncorrectRARPassword + self.needskip = False + res.append((info, reader.get_result())) + return res + + + def extract(self, checker, path, withSubpath, overwrite): + res = [] + for info in self.infoiter(): + checkres = checker(info) + if checkres!=False and not info.isdir: + if checkres==True: + fn = info.filename + if not withSubpath: + fn = os.path.split(fn)[-1] + target = os.path.join(path, fn) + else: + raise DeprecationWarning, "Condition callbacks returning strings are deprecated and only supported in Windows" + target = checkres + if overwrite or (not os.path.exists(target)): + tmpres = RARProcessFile(self._handle, RAR_EXTRACT, None, target) + if tmpres==ERAR_BAD_DATA: + raise IncorrectRARPassword + + self.needskip = False + res.append(info) + return res + + diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..0d9bd7c --- /dev/null +++ b/__init__.py @@ -0,0 +1 @@ +__author__ = 'dromanin' diff --git a/comet.py b/comet.py new file mode 100644 index 0000000..1a06977 --- /dev/null +++ b/comet.py @@ -0,0 +1,260 @@ +""" +A python class to encapsulate CoMet data +""" + +""" +Copyright 2012-2014 Anthony Beville + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +from datetime import datetime +import zipfile +from pprint import pprint +import xml.etree.ElementTree as ET +from genericmetadata import GenericMetadata +import utils + +class CoMet: + + writer_synonyms = ['writer', 'plotter', 'scripter'] + penciller_synonyms = [ 'artist', 'penciller', 'penciler', 'breakdowns' ] + inker_synonyms = [ 'inker', 'artist', 'finishes' ] + colorist_synonyms = [ 'colorist', 'colourist', 'colorer', 'colourer' ] + letterer_synonyms = [ 'letterer'] + cover_synonyms = [ 'cover', 'covers', 'coverartist', 'cover artist' ] + editor_synonyms = [ 'editor'] + + def metadataFromString( self, string ): + + tree = ET.ElementTree(ET.fromstring( string )) + return self.convertXMLToMetadata( tree ) + + def stringFromMetadata( self, metadata ): + + header = '\n' + + tree = self.convertMetadataToXML( self, metadata ) + return header + ET.tostring(tree.getroot()) + + def indent( self, elem, level=0 ): + # for making the XML output readable + i = "\n" + level*" " + if len(elem): + if not elem.text or not elem.text.strip(): + elem.text = i + " " + if not elem.tail or not elem.tail.strip(): + elem.tail = i + for elem in elem: + self.indent( elem, level+1 ) + if not elem.tail or not elem.tail.strip(): + elem.tail = i + else: + if level and (not elem.tail or not elem.tail.strip()): + elem.tail = i + + def convertMetadataToXML( self, filename, metadata ): + + #shorthand for the metadata + md = metadata + + # build a tree structure + root = ET.Element("comet") + root.attrib['xmlns:comet'] = "http://www.denvog.com/comet/" + root.attrib['xmlns:xsi'] = "http://www.w3.org/2001/XMLSchema-instance" + root.attrib['xsi:schemaLocation'] = "http://www.denvog.com http://www.denvog.com/comet/comet.xsd" + + #helper func + def assign( comet_entry, md_entry): + if md_entry is not None: + ET.SubElement(root, comet_entry).text = u"{0}".format(md_entry) + + # title is manditory + if md.title is None: + md.title = "" + assign( 'title', md.title ) + assign( 'series', md.series ) + assign( 'issue', md.issue ) #must be int?? + assign( 'volume', md.volume ) + assign( 'description', md.comments ) + assign( 'publisher', md.publisher ) + assign( 'pages', md.pageCount ) + assign( 'format', md.format ) + assign( 'language', md.language ) + assign( 'rating', md.maturityRating ) + assign( 'price', md.price ) + assign( 'isVersionOf', md.isVersionOf ) + assign( 'rights', md.rights ) + assign( 'identifier', md.identifier ) + assign( 'lastMark', md.lastMark ) + assign( 'genre', md.genre ) # TODO repeatable + + if md.characters is not None: + char_list = [ c.strip() for c in md.characters.split(',') ] + for c in char_list: + assign( 'character', c ) + + if md.manga is not None and md.manga == "YesAndRightToLeft": + assign( 'readingDirection', "rtl") + + date_str = "" + if md.year is not None: + date_str = str(md.year).zfill(4) + if md.month is not None: + date_str += "-" + str(md.month).zfill(2) + assign( 'date', date_str ) + + assign( 'coverImage', md.coverImage ) + + # need to specially process the credits, since they are structured differently than CIX + credit_writer_list = list() + credit_penciller_list = list() + credit_inker_list = list() + credit_colorist_list = list() + credit_letterer_list = list() + credit_cover_list = list() + credit_editor_list = list() + + # loop thru credits, and build a list for each role that CoMet supports + for credit in metadata.credits: + + if credit['role'].lower() in set( self.writer_synonyms ): + ET.SubElement(root, 'writer').text = u"{0}".format(credit['person']) + + if credit['role'].lower() in set( self.penciller_synonyms ): + ET.SubElement(root, 'penciller').text = u"{0}".format(credit['person']) + + if credit['role'].lower() in set( self.inker_synonyms ): + ET.SubElement(root, 'inker').text = u"{0}".format(credit['person']) + + if credit['role'].lower() in set( self.colorist_synonyms ): + ET.SubElement(root, 'colorist').text = u"{0}".format(credit['person']) + + if credit['role'].lower() in set( self.letterer_synonyms ): + ET.SubElement(root, 'letterer').text = u"{0}".format(credit['person']) + + if credit['role'].lower() in set( self.cover_synonyms ): + ET.SubElement(root, 'coverDesigner').text = u"{0}".format(credit['person']) + + if credit['role'].lower() in set( self.editor_synonyms ): + ET.SubElement(root, 'editor').text = u"{0}".format(credit['person']) + + + # self pretty-print + self.indent(root) + + # wrap it in an ElementTree instance, and save as XML + tree = ET.ElementTree(root) + return tree + + + def convertXMLToMetadata( self, tree ): + + root = tree.getroot() + + if root.tag != 'comet': + raise 1 + return None + + metadata = GenericMetadata() + md = metadata + + # Helper function + def xlate( tag ): + node = root.find( tag ) + if node is not None: + return node.text + else: + return None + + md.series = xlate( 'series' ) + md.title = xlate( 'title' ) + md.issue = xlate( 'issue' ) + md.volume = xlate( 'volume' ) + md.comments = xlate( 'description' ) + md.publisher = xlate( 'publisher' ) + md.language = xlate( 'language' ) + md.format = xlate( 'format' ) + md.pageCount = xlate( 'pages' ) + md.maturityRating = xlate( 'rating' ) + md.price = xlate( 'price' ) + md.isVersionOf = xlate( 'isVersionOf' ) + md.rights = xlate( 'rights' ) + md.identifier = xlate( 'identifier' ) + md.lastMark = xlate( 'lastMark' ) + md.genre = xlate( 'genre' ) # TODO - repeatable field + + date = xlate( 'date' ) + if date is not None: + parts = date.split('-') + if len( parts) > 0: + md.year = parts[0] + if len( parts) > 1: + md.month = parts[1] + + md.coverImage = xlate( 'coverImage' ) + + readingDirection = xlate( 'readingDirection' ) + if readingDirection is not None and readingDirection == "rtl": + md.manga = "YesAndRightToLeft" + + # loop for character tags + char_list = [] + for n in root: + if n.tag == 'character': + char_list.append(n.text.strip()) + md.characters = utils.listToString( char_list ) + + # Now extract the credit info + for n in root: + if ( n.tag == 'writer' or + n.tag == 'penciller' or + n.tag == 'inker' or + n.tag == 'colorist' or + n.tag == 'letterer' or + n.tag == 'editor' + ): + metadata.addCredit( n.text.strip(), n.tag.title() ) + + if n.tag == 'coverDesigner': + metadata.addCredit( n.text.strip(), "Cover" ) + + + metadata.isEmpty = False + + return metadata + + #verify that the string actually contains CoMet data in XML format + def validateString( self, string ): + try: + tree = ET.ElementTree(ET.fromstring( string )) + root = tree.getroot() + if root.tag != 'comet': + raise Exception + except: + return False + + return True + + + def writeToExternalFile( self, filename, metadata ): + + tree = self.convertMetadataToXML( self, metadata ) + #ET.dump(tree) + tree.write(filename, encoding='utf-8') + + def readFromExternalFile( self, filename ): + + tree = ET.parse( filename ) + return self.convertXMLToMetadata( tree ) + diff --git a/comicarchive.py b/comicarchive.py new file mode 100644 index 0000000..381dc68 --- /dev/null +++ b/comicarchive.py @@ -0,0 +1,1088 @@ +""" +A python class to represent a single comic, be it file or folder of images +""" + +""" +Copyright 2012-2014 Anthony Beville + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +import zipfile +import os +import struct +import sys +import tempfile +import subprocess +import platform +import locale +from natsort import natsorted + +if platform.system() == "Windows": + import _subprocess +import time + +import StringIO +try: + import Image + pil_available = True +except ImportError: + pil_available = False + +sys.path.insert(0, os.path.abspath(".") ) +import UnRAR2 +from UnRAR2.rar_exceptions import * + +#from settings import ComicTaggerSettings +from comicinfoxml import ComicInfoXml +from comicbookinfo import ComicBookInfo +from comet import CoMet +from genericmetadata import GenericMetadata, PageType +from filenameparser import FileNameParser +from PyPDF2 import PdfFileReader + +class MetaDataStyle: + CBI = 0 + CIX = 1 + COMET = 2 + name = [ 'ComicBookLover', 'ComicRack', 'CoMet' ] + +class ZipArchiver: + + def __init__( self, path ): + self.path = path + + def getArchiveComment( self ): + zf = zipfile.ZipFile( self.path, 'r' ) + comment = zf.comment + zf.close() + return comment + + def setArchiveComment( self, comment ): + return self.writeZipComment( self.path, comment ) + + def readArchiveFile( self, archive_file ): + data = "" + zf = zipfile.ZipFile( self.path, 'r' ) + + try: + data = zf.read( archive_file ) + except zipfile.BadZipfile as e: + print >> sys.stderr, u"bad zipfile [{0}]: {1} :: {2}".format(e, self.path, archive_file) + zf.close() + raise IOError + except Exception as e: + zf.close() + print >> sys.stderr, u"bad zipfile [{0}]: {1} :: {2}".format(e, self.path, archive_file) + raise IOError + finally: + zf.close() + return data + + def removeArchiveFile( self, archive_file ): + try: + self.rebuildZipFile( [ archive_file ] ) + except: + return False + else: + return True + + def writeArchiveFile( self, archive_file, data ): + # At the moment, no other option but to rebuild the whole + # zip archive w/o the indicated file. Very sucky, but maybe + # another solution can be found + try: + self.rebuildZipFile( [ archive_file ] ) + + #now just add the archive file as a new one + zf = zipfile.ZipFile(self.path, mode='a', compression=zipfile.ZIP_DEFLATED ) + zf.writestr( archive_file, data ) + zf.close() + return True + except: + return False + + def getArchiveFilenameList( self ): + try: + zf = zipfile.ZipFile( self.path, 'r' ) + namelist = zf.namelist() + zf.close() + return namelist + except Exception as e: + print >> sys.stderr, u"Unable to get zipfile list [{0}]: {1}".format(e, self.path) + return [] + + # zip helper func + def rebuildZipFile( self, exclude_list ): + + # this recompresses the zip archive, without the files in the exclude_list + #print ">> sys.stderr, Rebuilding zip {0} without {1}".format( self.path, exclude_list ) + + # generate temp file + tmp_fd, tmp_name = tempfile.mkstemp( dir=os.path.dirname(self.path) ) + os.close( tmp_fd ) + + zin = zipfile.ZipFile (self.path, 'r') + zout = zipfile.ZipFile (tmp_name, 'w') + for item in zin.infolist(): + buffer = zin.read(item.filename) + if ( item.filename not in exclude_list ): + zout.writestr(item, buffer) + + #preserve the old comment + zout.comment = zin.comment + + zout.close() + zin.close() + + # replace with the new file + os.remove( self.path ) + os.rename( tmp_name, self.path ) + + + def writeZipComment( self, filename, comment ): + """ + This is a custom function for writing a comment to a zip file, + since the built-in one doesn't seem to work on Windows and Mac OS/X + + Fortunately, the zip comment is at the end of the file, and it's + easy to manipulate. See this website for more info: + see: http://en.wikipedia.org/wiki/Zip_(file_format)#Structure + """ + + #get file size + statinfo = os.stat(filename) + file_length = statinfo.st_size + + try: + fo = open(filename, "r+b") + + #the starting position, relative to EOF + pos = -4 + + found = False + value = bytearray() + + # walk backwards to find the "End of Central Directory" record + while ( not found ) and ( -pos != file_length ): + # seek, relative to EOF + fo.seek( pos, 2) + + value = fo.read( 4 ) + + #look for the end of central directory signature + if bytearray(value) == bytearray([ 0x50, 0x4b, 0x05, 0x06 ]): + found = True + else: + # not found, step back another byte + pos = pos - 1 + #print pos,"{1} int: {0:x}".format(bytearray(value)[0], value) + + if found: + + # now skip forward 20 bytes to the comment length word + pos += 20 + fo.seek( pos, 2) + + # Pack the length of the comment string + format = "H" # one 2-byte integer + comment_length = struct.pack(format, len(comment)) # pack integer in a binary string + + # write out the length + fo.write( comment_length ) + fo.seek( pos+2, 2) + + # write out the comment itself + fo.write( comment ) + fo.truncate() + fo.close() + else: + raise Exception('Failed to write comment to zip file!') + except: + return False + else: + return True + + def copyFromArchive( self, otherArchive ): + # Replace the current zip with one copied from another archive + try: + zout = zipfile.ZipFile (self.path, 'w') + for fname in otherArchive.getArchiveFilenameList(): + data = otherArchive.readArchiveFile( fname ) + if data is not None: + zout.writestr( fname, data ) + zout.close() + + #preserve the old comment + comment = otherArchive.getArchiveComment() + if comment is not None: + if not self.writeZipComment( self.path, comment ): + return False + except Exception as e: + print >> sys.stderr, u"Error while copying to {0}: {1}".format(self.path, e) + return False + else: + return True + + +#------------------------------------------ +# RAR implementation + +class RarArchiver: + + devnull = None + def __init__( self, path, rar_exe_path ): + self.path = path + self.rar_exe_path = rar_exe_path + + if RarArchiver.devnull is None: + RarArchiver.devnull = open(os.devnull, "w") + + # windows only, keeps the cmd.exe from popping up + if platform.system() == "Windows": + self.startupinfo = subprocess.STARTUPINFO() + self.startupinfo.dwFlags |= _subprocess.STARTF_USESHOWWINDOW + else: + self.startupinfo = None + + def __del__(self): + #RarArchiver.devnull.close() + pass + + def getArchiveComment( self ): + + rarc = self.getRARObj() + return rarc.comment + + def setArchiveComment( self, comment ): + + if self.rar_exe_path is not None: + try: + # write comment to temp file + tmp_fd, tmp_name = tempfile.mkstemp() + f = os.fdopen(tmp_fd, 'w+b') + f.write( comment ) + f.close() + + working_dir = os.path.dirname( os.path.abspath( self.path ) ) + + # use external program to write comment to Rar archive + subprocess.call([self.rar_exe_path, 'c', '-w' + working_dir , '-c-', '-z' + tmp_name, self.path], + startupinfo=self.startupinfo, + stdout=RarArchiver.devnull) + + if platform.system() == "Darwin": + time.sleep(1) + + os.remove( tmp_name) + except: + return False + else: + return True + else: + return False + + def readArchiveFile( self, archive_file ): + + # Make sure to escape brackets, since some funky stuff is going on + # underneath with "fnmatch" + archive_file = archive_file.replace("[", '[[]') + entries = [] + + rarc = self.getRARObj() + + tries = 0 + while tries < 7: + try: + tries = tries+1 + entries = rarc.read_files( archive_file ) + + if entries[0][0].size != len(entries[0][1]): + print >> sys.stderr, u"readArchiveFile(): [file is not expected size: {0} vs {1}] {2}:{3} [attempt # {4}]".format( + entries[0][0].size,len(entries[0][1]), self.path, archive_file, tries) + continue + + except (OSError, IOError) as e: + print >> sys.stderr, u"readArchiveFile(): [{0}] {1}:{2} attempt#{3}".format(str(e), self.path, archive_file, tries) + time.sleep(1) + except Exception as e: + print >> sys.stderr, u"Unexpected exception in readArchiveFile(): [{0}] for {1}:{2} attempt#{3}".format(str(e), self.path, archive_file, tries) + break + + else: + #Success" + #entries is a list of of tuples: ( rarinfo, filedata) + if tries > 1: + print >> sys.stderr, u"Attempted read_files() {0} times".format(tries) + if (len(entries) == 1): + return entries[0][1] + else: + raise IOError + + raise IOError + + + + def writeArchiveFile( self, archive_file, data ): + + if self.rar_exe_path is not None: + try: + tmp_folder = tempfile.mkdtemp() + + tmp_file = os.path.join( tmp_folder, archive_file ) + + working_dir = os.path.dirname( os.path.abspath( self.path ) ) + + # TODO: will this break if 'archive_file' is in a subfolder. i.e. "foo/bar.txt" + # will need to create the subfolder above, I guess... + f = open(tmp_file, 'w') + f.write( data ) + f.close() + + # use external program to write file to Rar archive + subprocess.call([self.rar_exe_path, 'a', '-w' + working_dir ,'-c-', '-ep', self.path, tmp_file], + startupinfo=self.startupinfo, + stdout=RarArchiver.devnull) + + if platform.system() == "Darwin": + time.sleep(1) + os.remove( tmp_file) + os.rmdir( tmp_folder) + except: + return False + else: + return True + else: + return False + + def removeArchiveFile( self, archive_file ): + if self.rar_exe_path is not None: + try: + # use external program to remove file from Rar archive + subprocess.call([self.rar_exe_path, 'd','-c-', self.path, archive_file], + startupinfo=self.startupinfo, + stdout=RarArchiver.devnull) + + if platform.system() == "Darwin": + time.sleep(1) + except: + return False + else: + return True + else: + return False + + def getArchiveFilenameList( self ): + + rarc = self.getRARObj() + #namelist = [ item.filename for item in rarc.infolist() ] + #return namelist + + tries = 0 + while tries < 7: + try: + tries = tries+1 + #namelist = [ item.filename for item in rarc.infolist() ] + namelist = [] + for item in rarc.infolist(): + if item.size != 0: + namelist.append( item.filename ) + + except (OSError, IOError) as e: + print >> sys.stderr, u"getArchiveFilenameList(): [{0}] {1} attempt#{2}".format(str(e), self.path, tries) + time.sleep(1) + + else: + #Success" + return namelist + + raise e + + + def getRARObj( self ): + tries = 0 + while tries < 7: + try: + tries = tries+1 + rarc = UnRAR2.RarFile( self.path ) + + except (OSError, IOError) as e: + print >> sys.stderr, u"getRARObj(): [{0}] {1} attempt#{2}".format(str(e), self.path, tries) + time.sleep(1) + + else: + #Success" + return rarc + + raise e + +#------------------------------------------ +# Folder implementation +class FolderArchiver: + + def __init__( self, path ): + self.path = path + self.comment_file_name = "ComicTaggerFolderComment.txt" + + def getArchiveComment( self ): + return self.readArchiveFile( self.comment_file_name ) + + def setArchiveComment( self, comment ): + return self.writeArchiveFile( self.comment_file_name, comment ) + + def readArchiveFile( self, archive_file ): + + data = "" + fname = os.path.join( self.path, archive_file ) + try: + with open( fname, 'rb' ) as f: + data = f.read() + f.close() + except IOError as e: + pass + + return data + + def writeArchiveFile( self, archive_file, data ): + + fname = os.path.join( self.path, archive_file ) + try: + with open(fname, 'w+') as f: + f.write( data ) + f.close() + except: + return False + else: + return True + + def removeArchiveFile( self, archive_file ): + + fname = os.path.join( self.path, archive_file ) + try: + os.remove( fname ) + except: + return False + else: + return True + + def getArchiveFilenameList( self ): + return self.listFiles( self.path ) + + def listFiles( self, folder ): + + itemlist = list() + + for item in os.listdir( folder ): + itemlist.append( item ) + if os.path.isdir( item ): + itemlist.extend( self.listFiles( os.path.join( folder, item ) )) + + return itemlist + +#------------------------------------------ +# Unknown implementation +class UnknownArchiver: + + def __init__( self, path ): + self.path = path + + def getArchiveComment( self ): + return "" + def setArchiveComment( self, comment ): + return False + def readArchiveFile( self ): + return "" + def writeArchiveFile( self, archive_file, data ): + return False + def removeArchiveFile( self, archive_file ): + return False + def getArchiveFilenameList( self ): + return [] + +class PdfArchiver: + def __init__( self, path ): + self.path = path + + def getArchiveComment( self ): + return "" + def setArchiveComment( self, comment ): + return False + def readArchiveFile( self, page_num ): + return subprocess.check_output(['mudraw', '-o','-', self.path, str(int(os.path.basename(page_num)[:-4]))]) + def writeArchiveFile( self, archive_file, data ): + return False + def removeArchiveFile( self, archive_file ): + return False + def getArchiveFilenameList( self ): + out = [] + pdf = PdfFileReader(open(self.path, 'rb')) + for page in range(1, pdf.getNumPages() + 1): + out.append("/%04d.jpg" % (page)) + return out + +#------------------------------------------------------------------ +class ComicArchive: + + logo_data = None + + class ArchiveType: + Zip, Rar, Folder, Pdf, Unknown = range(5) + + def __init__( self, path, rar_exe_path=None, default_image_path=None ): + self.path = path + + self.rar_exe_path = rar_exe_path + self.ci_xml_filename = 'ComicInfo.xml' + self.comet_default_filename = 'CoMet.xml' + self.resetCache() + self.default_image_path = default_image_path + + # Use file extension to decide which archive test we do first + ext = os.path.splitext(path)[1].lower() + + self.archive_type = self.ArchiveType.Unknown + self.archiver = UnknownArchiver( self.path ) + + if ext == ".cbr" or ext == ".rar": + if self.rarTest(): + self.archive_type = self.ArchiveType.Rar + self.archiver = RarArchiver( self.path, rar_exe_path=self.rar_exe_path ) + + elif self.zipTest(): + self.archive_type = self.ArchiveType.Zip + self.archiver = ZipArchiver( self.path ) + else: + if self.zipTest(): + self.archive_type = self.ArchiveType.Zip + self.archiver = ZipArchiver( self.path ) + + elif self.rarTest(): + self.archive_type = self.ArchiveType.Rar + self.archiver = RarArchiver( self.path, rar_exe_path=self.rar_exe_path ) + elif os.path.basename(self.path)[-3:] == 'pdf': + self.archive_type = self.ArchiveType.Pdf + self.archiver = PdfArchiver(self.path) + + if ComicArchive.logo_data is None: + #fname = ComicTaggerSettings.getGraphic('nocover.png') + fname = self.default_image_path + with open(fname, 'rb') as fd: + ComicArchive.logo_data = fd.read() + + # Clears the cached data + def resetCache( self ): + self.has_cix = None + self.has_cbi = None + self.has_comet = None + self.comet_filename = None + self.page_count = None + self.page_list = None + self.cix_md = None + self.cbi_md = None + self.comet_md = None + + def loadCache( self, style_list ): + for style in style_list: + self.readMetadata(style) + + def rename( self, path ): + self.path = path + self.archiver.path = path + + def zipTest( self ): + return zipfile.is_zipfile( self.path ) + + def rarTest( self ): + try: + rarc = UnRAR2.RarFile( self.path ) + except: # InvalidRARArchive: + return False + else: + return True + + + def isZip( self ): + return self.archive_type == self.ArchiveType.Zip + + def isRar( self ): + return self.archive_type == self.ArchiveType.Rar + def isPdf(self): + return self.archive_type == self.ArchiveType.Pdf + def isFolder( self ): + return self.archive_type == self.ArchiveType.Folder + + def isWritable( self, check_rar_status=True ): + if self.archive_type == self.ArchiveType.Unknown : + return False + + elif check_rar_status and self.isRar() and self.rar_exe_path is None: + return False + + elif not os.access(self.path, os.W_OK): + return False + + elif ((self.archive_type != self.ArchiveType.Folder) and + (not os.access( os.path.dirname( os.path.abspath(self.path)), os.W_OK ))): + return False + + return True + + def isWritableForStyle( self, data_style ): + + if self.isRar() and data_style == MetaDataStyle.CBI: + return False + + return self.isWritable() + + def seemsToBeAComicArchive( self ): + + # Do we even care about extensions?? + ext = os.path.splitext(self.path)[1].lower() + + if ( + ( self.isZip() or self.isRar() or self.isPdf()) #or self.isFolder() ) + and + ( self.getNumberOfPages() > 0) + + ): + return True + else: + return False + + def readMetadata( self, style ): + + if style == MetaDataStyle.CIX: + return self.readCIX() + elif style == MetaDataStyle.CBI: + return self.readCBI() + elif style == MetaDataStyle.COMET: + return self.readCoMet() + else: + return GenericMetadata() + + def writeMetadata( self, metadata, style ): + + retcode = None + if style == MetaDataStyle.CIX: + retcode = self.writeCIX( metadata ) + elif style == MetaDataStyle.CBI: + retcode = self.writeCBI( metadata ) + elif style == MetaDataStyle.COMET: + retcode = self.writeCoMet( metadata ) + return retcode + + + def hasMetadata( self, style ): + + if style == MetaDataStyle.CIX: + return self.hasCIX() + elif style == MetaDataStyle.CBI: + return self.hasCBI() + elif style == MetaDataStyle.COMET: + return self.hasCoMet() + else: + return False + + def removeMetadata( self, style ): + retcode = True + if style == MetaDataStyle.CIX: + retcode = self.removeCIX() + elif style == MetaDataStyle.CBI: + retcode = self.removeCBI() + elif style == MetaDataStyle.COMET: + retcode = self.removeCoMet() + return retcode + + def getPage( self, index ): + + image_data = None + + filename = self.getPageName( index ) + + if filename is not None: + try: + image_data = self.archiver.readArchiveFile( filename ) + except IOError: + print >> sys.stderr, u"Error reading in page. Substituting logo page." + image_data = ComicArchive.logo_data + + return image_data + + def getPageName( self, index ): + + if index is None: + return None + + page_list = self.getPageNameList() + + num_pages = len( page_list ) + if num_pages == 0 or index >= num_pages: + return None + + return page_list[index] + + def getScannerPageIndex( self ): + + scanner_page_index = None + + #make a guess at the scanner page + name_list = self.getPageNameList() + count = self.getNumberOfPages() + + #too few pages to really know + if count < 5: + return None + + # count the length of every filename, and count occurences + length_buckets = dict() + for name in name_list: + fname = os.path.split(name)[1] + length = len(fname) + if length_buckets.has_key( length ): + length_buckets[ length ] += 1 + else: + length_buckets[ length ] = 1 + + # sort by most common + sorted_buckets = sorted(length_buckets.iteritems(), key=lambda (k,v): (v,k), reverse=True) + + # statistical mode occurence is first + mode_length = sorted_buckets[0][0] + + # we are only going to consider the final image file: + final_name = os.path.split(name_list[count-1])[1] + + common_length_list = list() + for name in name_list: + if len(os.path.split(name)[1]) == mode_length: + common_length_list.append( os.path.split(name)[1] ) + + prefix = os.path.commonprefix(common_length_list) + + if mode_length <= 7 and prefix == "": + #probably all numbers + if len(final_name) > mode_length: + scanner_page_index = count-1 + + # see if the last page doesn't start with the same prefix as most others + elif not final_name.startswith(prefix): + scanner_page_index = count-1 + + return scanner_page_index + + + def getPageNameList( self , sort_list=True): + + if self.page_list is None: + # get the list file names in the archive, and sort + files = self.archiver.getArchiveFilenameList() + + # seems like some archive creators are on Windows, and don't know about case-sensitivity! + if sort_list: + def keyfunc(k): + #hack to account for some weird scanner ID pages + #basename=os.path.split(k)[1] + #if basename < '0': + # k = os.path.join(os.path.split(k)[0], "z" + basename) + return k.lower() + + files = natsorted(files, key=keyfunc,signed=False) + + # make a sub-list of image files + self.page_list = [] + for name in files: + if ( name[-4:].lower() in [ ".jpg", "jpeg", ".png", ".gif", "webp" ] and os.path.basename(name)[0] != "." ): + self.page_list.append(name) + + return self.page_list + + def getNumberOfPages( self ): + + if self.page_count is None: + self.page_count = len( self.getPageNameList( ) ) + return self.page_count + + def readCBI( self ): + if self.cbi_md is None: + raw_cbi = self.readRawCBI() + if raw_cbi is None: + self.cbi_md = GenericMetadata() + else: + self.cbi_md = ComicBookInfo().metadataFromString( raw_cbi ) + + self.cbi_md.setDefaultPageList( self.getNumberOfPages() ) + + return self.cbi_md + + def readRawCBI( self ): + if ( not self.hasCBI() ): + return None + + return self.archiver.getArchiveComment() + + def hasCBI(self): + if self.has_cbi is None: + + #if ( not ( self.isZip() or self.isRar()) or not self.seemsToBeAComicArchive() ): + if not self.seemsToBeAComicArchive(): + self.has_cbi = False + else: + comment = self.archiver.getArchiveComment() + self.has_cbi = ComicBookInfo().validateString( comment ) + + return self.has_cbi + + def writeCBI( self, metadata ): + if metadata is not None: + self.applyArchiveInfoToMetadata( metadata ) + cbi_string = ComicBookInfo().stringFromMetadata( metadata ) + write_success = self.archiver.setArchiveComment( cbi_string ) + if write_success: + self.has_cbi = True + self.cbi_md = metadata + self.resetCache() + return write_success + else: + return False + + def removeCBI( self ): + if self.hasCBI(): + write_success = self.archiver.setArchiveComment( "" ) + if write_success: + self.has_cbi = False + self.cbi_md = None + self.resetCache() + return write_success + return True + + def readCIX( self ): + if self.cix_md is None: + raw_cix = self.readRawCIX() + if raw_cix is None or raw_cix == "": + self.cix_md = GenericMetadata() + else: + self.cix_md = ComicInfoXml().metadataFromString( raw_cix ) + + #validate the existing page list (make sure count is correct) + if len ( self.cix_md.pages ) != 0 : + if len ( self.cix_md.pages ) != self.getNumberOfPages(): + # pages array doesn't match the actual number of images we're seeing + # in the archive, so discard the data + self.cix_md.pages = [] + + if len( self.cix_md.pages ) == 0: + self.cix_md.setDefaultPageList( self.getNumberOfPages() ) + + return self.cix_md + + def readRawCIX( self ): + if not self.hasCIX(): + return None + try: + raw_cix = self.archiver.readArchiveFile( self.ci_xml_filename ) + except IOError: + print "Error reading in raw CIX!" + raw_cix = "" + return raw_cix + + def writeCIX(self, metadata): + + if metadata is not None: + self.applyArchiveInfoToMetadata( metadata, calc_page_sizes=True ) + cix_string = ComicInfoXml().stringFromMetadata( metadata ) + write_success = self.archiver.writeArchiveFile( self.ci_xml_filename, cix_string ) + if write_success: + self.has_cix = True + self.cix_md = metadata + self.resetCache() + return write_success + else: + return False + + def removeCIX( self ): + if self.hasCIX(): + write_success = self.archiver.removeArchiveFile( self.ci_xml_filename ) + if write_success: + self.has_cix = False + self.cix_md = None + self.resetCache() + return write_success + return True + + + def hasCIX(self): + if self.has_cix is None: + + if not self.seemsToBeAComicArchive(): + self.has_cix = False + elif self.ci_xml_filename in self.archiver.getArchiveFilenameList(): + self.has_cix = True + else: + self.has_cix = False + return self.has_cix + + + def readCoMet( self ): + if self.comet_md is None: + raw_comet = self.readRawCoMet() + if raw_comet is None or raw_comet == "": + self.comet_md = GenericMetadata() + else: + self.comet_md = CoMet().metadataFromString( raw_comet ) + + self.comet_md.setDefaultPageList( self.getNumberOfPages() ) + #use the coverImage value from the comet_data to mark the cover in this struct + # walk through list of images in file, and find the matching one for md.coverImage + # need to remove the existing one in the default + if self.comet_md.coverImage is not None: + cover_idx = 0 + for idx,f in enumerate(self.getPageNameList()): + if self.comet_md.coverImage == f: + cover_idx = idx + break + if cover_idx != 0: + del (self.comet_md.pages[0]['Type'] ) + self.comet_md.pages[ cover_idx ]['Type'] = PageType.FrontCover + + return self.comet_md + + def readRawCoMet( self ): + if not self.hasCoMet(): + print >> sys.stderr, self.path, "doesn't have CoMet data!" + return None + + try: + raw_comet = self.archiver.readArchiveFile( self.comet_filename ) + except IOError: + print >> sys.stderr, u"Error reading in raw CoMet!" + raw_comet = "" + return raw_comet + + def writeCoMet(self, metadata): + + if metadata is not None: + if not self.hasCoMet(): + self.comet_filename = self.comet_default_filename + + self.applyArchiveInfoToMetadata( metadata ) + # Set the coverImage value, if it's not the first page + cover_idx = int(metadata.getCoverPageIndexList()[0]) + if cover_idx != 0: + metadata.coverImage = self.getPageName( cover_idx ) + + comet_string = CoMet().stringFromMetadata( metadata ) + write_success = self.archiver.writeArchiveFile( self.comet_filename, comet_string ) + if write_success: + self.has_comet = True + self.comet_md = metadata + self.resetCache() + return write_success + else: + return False + + def removeCoMet( self ): + if self.hasCoMet(): + write_success = self.archiver.removeArchiveFile( self.comet_filename ) + if write_success: + self.has_comet = False + self.comet_md = None + self.resetCache() + return write_success + return True + + def hasCoMet(self): + if self.has_comet is None: + self.has_comet = False + if not self.seemsToBeAComicArchive(): + return self.has_comet + + #look at all xml files in root, and search for CoMet data, get first + for n in self.archiver.getArchiveFilenameList(): + if ( os.path.dirname(n) == "" and + os.path.splitext(n)[1].lower() == '.xml'): + # read in XML file, and validate it + try: + data = self.archiver.readArchiveFile( n ) + except: + data = "" + print >> sys.stderr, u"Error reading in Comet XML for validation!" + if CoMet().validateString( data ): + # since we found it, save it! + self.comet_filename = n + self.has_comet = True + break + + return self.has_comet + + + + def applyArchiveInfoToMetadata( self, md, calc_page_sizes=False): + md.pageCount = self.getNumberOfPages() + + if calc_page_sizes: + for p in md.pages: + idx = int( p['Image'] ) + if pil_available: + if 'ImageSize' not in p or 'ImageHeight' not in p or 'ImageWidth' not in p: + data = self.getPage( idx ) + if data is not None: + try: + im = Image.open(StringIO.StringIO(data)) + w,h = im.size + + p['ImageSize'] = str(len(data)) + p['ImageHeight'] = str(h) + p['ImageWidth'] = str(w) + except IOError: + p['ImageSize'] = str(len(data)) + + else: + if 'ImageSize' not in p: + data = self.getPage( idx ) + p['ImageSize'] = str(len(data)) + + + + def metadataFromFilename( self , parse_scan_info=True): + + metadata = GenericMetadata() + + fnp = FileNameParser() + fnp.parseFilename( self.path ) + + if fnp.issue != "": + metadata.issue = fnp.issue + if fnp.series != "": + metadata.series = fnp.series + if fnp.volume != "": + metadata.volume = fnp.volume + if fnp.year != "": + metadata.year = fnp.year + if fnp.issue_count != "": + metadata.issueCount = fnp.issue_count + if parse_scan_info: + if fnp.remainder != "": + metadata.scanInfo = fnp.remainder + + metadata.isEmpty = False + + return metadata + + def exportAsZip( self, zipfilename ): + if self.archive_type == self.ArchiveType.Zip: + # nothing to do, we're already a zip + return True + + zip_archiver = ZipArchiver( zipfilename ) + return zip_archiver.copyFromArchive( self.archiver ) + diff --git a/comicbookinfo.py b/comicbookinfo.py new file mode 100644 index 0000000..a0bbaf0 --- /dev/null +++ b/comicbookinfo.py @@ -0,0 +1,152 @@ +""" +A python class to encapsulate the ComicBookInfo data +""" + +""" +Copyright 2012-2014 Anthony Beville + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + + +import json +from datetime import datetime +import zipfile + +from genericmetadata import GenericMetadata +import utils +#import ctversion + +class ComicBookInfo: + + + def metadataFromString( self, string ): + + cbi_container = json.loads( unicode(string, 'utf-8') ) + + metadata = GenericMetadata() + + cbi = cbi_container[ 'ComicBookInfo/1.0' ] + + #helper func + # If item is not in CBI, return None + def xlate( cbi_entry): + if cbi_entry in cbi: + return cbi[cbi_entry] + else: + return None + + metadata.series = xlate( 'series' ) + metadata.title = xlate( 'title' ) + metadata.issue = xlate( 'issue' ) + metadata.publisher = xlate( 'publisher' ) + metadata.month = xlate( 'publicationMonth' ) + metadata.year = xlate( 'publicationYear' ) + metadata.issueCount = xlate( 'numberOfIssues' ) + metadata.comments = xlate( 'comments' ) + metadata.credits = xlate( 'credits' ) + metadata.genre = xlate( 'genre' ) + metadata.volume = xlate( 'volume' ) + metadata.volumeCount = xlate( 'numberOfVolumes' ) + metadata.language = xlate( 'language' ) + metadata.country = xlate( 'country' ) + metadata.criticalRating = xlate( 'rating' ) + metadata.tags = xlate( 'tags' ) + + # make sure credits and tags are at least empty lists and not None + if metadata.credits is None: + metadata.credits = [] + if metadata.tags is None: + metadata.tags = [] + + #need to massage the language string to be ISO + if metadata.language is not None: + # reverse look-up + pattern = metadata.language + metadata.language = None + for key in utils.getLanguageDict(): + if utils.getLanguageDict()[ key ] == pattern.encode('utf-8'): + metadata.language = key + break + + metadata.isEmpty = False + + return metadata + + def stringFromMetadata( self, metadata ): + + cbi_container = self.createJSONDictionary( metadata ) + return json.dumps( cbi_container ) + + #verify that the string actually contains CBI data in JSON format + def validateString( self, string ): + + try: + cbi_container = json.loads( string ) + except: + return False + + return ( 'ComicBookInfo/1.0' in cbi_container ) + + + def createJSONDictionary( self, metadata ): + + # Create the dictionary that we will convert to JSON text + cbi = dict() + cbi_container = {'appID' : 'ComicTagger/' + '1.0.0', #ctversion.version, + 'lastModified' : str(datetime.now()), + 'ComicBookInfo/1.0' : cbi } + + #helper func + def assign( cbi_entry, md_entry): + if md_entry is not None: + cbi[cbi_entry] = md_entry + + #helper func + def toInt(s): + i = None + if type(s) in [ str, unicode, int ]: + try: + i = int(s) + except ValueError: + pass + return i + + assign( 'series', metadata.series ) + assign( 'title', metadata.title ) + assign( 'issue', metadata.issue ) + assign( 'publisher', metadata.publisher ) + assign( 'publicationMonth', toInt(metadata.month) ) + assign( 'publicationYear', toInt(metadata.year) ) + assign( 'numberOfIssues', toInt(metadata.issueCount) ) + assign( 'comments', metadata.comments ) + assign( 'genre', metadata.genre ) + assign( 'volume', toInt(metadata.volume) ) + assign( 'numberOfVolumes', toInt(metadata.volumeCount) ) + assign( 'language', utils.getLanguageFromISO(metadata.language) ) + assign( 'country', metadata.country ) + assign( 'rating', metadata.criticalRating ) + assign( 'credits', metadata.credits ) + assign( 'tags', metadata.tags ) + + return cbi_container + + + def writeToExternalFile( self, filename, metadata ): + + cbi_container = self.createJSONDictionary(metadata) + + f = open(filename, 'w') + f.write(json.dumps(cbi_container, indent=4)) + f.close + diff --git a/comicinfoxml.py b/comicinfoxml.py new file mode 100644 index 0000000..9e9df07 --- /dev/null +++ b/comicinfoxml.py @@ -0,0 +1,293 @@ +""" +A python class to encapsulate ComicRack's ComicInfo.xml data +""" + +""" +Copyright 2012-2014 Anthony Beville + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +from datetime import datetime +import zipfile +from pprint import pprint +import xml.etree.ElementTree as ET +from genericmetadata import GenericMetadata +import utils + +class ComicInfoXml: + + writer_synonyms = ['writer', 'plotter', 'scripter'] + penciller_synonyms = [ 'artist', 'penciller', 'penciler', 'breakdowns' ] + inker_synonyms = [ 'inker', 'artist', 'finishes' ] + colorist_synonyms = [ 'colorist', 'colourist', 'colorer', 'colourer' ] + letterer_synonyms = [ 'letterer'] + cover_synonyms = [ 'cover', 'covers', 'coverartist', 'cover artist' ] + editor_synonyms = [ 'editor'] + + + def getParseableCredits( self ): + parsable_credits = [] + parsable_credits.extend( self.writer_synonyms ) + parsable_credits.extend( self.penciller_synonyms ) + parsable_credits.extend( self.inker_synonyms ) + parsable_credits.extend( self.colorist_synonyms ) + parsable_credits.extend( self.letterer_synonyms ) + parsable_credits.extend( self.cover_synonyms ) + parsable_credits.extend( self.editor_synonyms ) + return parsable_credits + + def metadataFromString( self, string ): + + tree = ET.ElementTree(ET.fromstring( string )) + return self.convertXMLToMetadata( tree ) + + def stringFromMetadata( self, metadata ): + + header = '\n' + + tree = self.convertMetadataToXML( self, metadata ) + return header + ET.tostring(tree.getroot()) + + def indent( self, elem, level=0 ): + # for making the XML output readable + i = "\n" + level*" " + if len(elem): + if not elem.text or not elem.text.strip(): + elem.text = i + " " + if not elem.tail or not elem.tail.strip(): + elem.tail = i + for elem in elem: + self.indent( elem, level+1 ) + if not elem.tail or not elem.tail.strip(): + elem.tail = i + else: + if level and (not elem.tail or not elem.tail.strip()): + elem.tail = i + + def convertMetadataToXML( self, filename, metadata ): + + #shorthand for the metadata + md = metadata + + # build a tree structure + root = ET.Element("ComicInfo") + root.attrib['xmlns:xsi']="http://www.w3.org/2001/XMLSchema-instance" + root.attrib['xmlns:xsd']="http://www.w3.org/2001/XMLSchema" + #helper func + def assign( cix_entry, md_entry): + if md_entry is not None: + ET.SubElement(root, cix_entry).text = u"{0}".format(md_entry) + + assign( 'Title', md.title ) + assign( 'Series', md.series ) + assign( 'Number', md.issue ) + assign( 'Count', md.issueCount ) + assign( 'Volume', md.volume ) + assign( 'AlternateSeries', md.alternateSeries ) + assign( 'AlternateNumber', md.alternateNumber ) + assign( 'StoryArc', md.storyArc ) + assign( 'SeriesGroup', md.seriesGroup ) + assign( 'AlternateCount', md.alternateCount ) + assign( 'Summary', md.comments ) + assign( 'Notes', md.notes ) + assign( 'Year', md.year ) + assign( 'Month', md.month ) + assign( 'Day', md.day ) + + # need to specially process the credits, since they are structured differently than CIX + credit_writer_list = list() + credit_penciller_list = list() + credit_inker_list = list() + credit_colorist_list = list() + credit_letterer_list = list() + credit_cover_list = list() + credit_editor_list = list() + + # first, loop thru credits, and build a list for each role that CIX supports + for credit in metadata.credits: + + if credit['role'].lower() in set( self.writer_synonyms ): + credit_writer_list.append(credit['person'].replace(",","")) + + if credit['role'].lower() in set( self.penciller_synonyms ): + credit_penciller_list.append(credit['person'].replace(",","")) + + if credit['role'].lower() in set( self.inker_synonyms ): + credit_inker_list.append(credit['person'].replace(",","")) + + if credit['role'].lower() in set( self.colorist_synonyms ): + credit_colorist_list.append(credit['person'].replace(",","")) + + if credit['role'].lower() in set( self.letterer_synonyms ): + credit_letterer_list.append(credit['person'].replace(",","")) + + if credit['role'].lower() in set( self.cover_synonyms ): + credit_cover_list.append(credit['person'].replace(",","")) + + if credit['role'].lower() in set( self.editor_synonyms ): + credit_editor_list.append(credit['person'].replace(",","")) + + # second, convert each list to string, and add to XML struct + if len( credit_writer_list ) > 0: + node = ET.SubElement(root, 'Writer') + node.text = utils.listToString( credit_writer_list ) + + if len( credit_penciller_list ) > 0: + node = ET.SubElement(root, 'Penciller') + node.text = utils.listToString( credit_penciller_list ) + + if len( credit_inker_list ) > 0: + node = ET.SubElement(root, 'Inker') + node.text = utils.listToString( credit_inker_list ) + + if len( credit_colorist_list ) > 0: + node = ET.SubElement(root, 'Colorist') + node.text = utils.listToString( credit_colorist_list ) + + if len( credit_letterer_list ) > 0: + node = ET.SubElement(root, 'Letterer') + node.text = utils.listToString( credit_letterer_list ) + + if len( credit_cover_list ) > 0: + node = ET.SubElement(root, 'CoverArtist') + node.text = utils.listToString( credit_cover_list ) + + if len( credit_editor_list ) > 0: + node = ET.SubElement(root, 'Editor') + node.text = utils.listToString( credit_editor_list ) + + assign( 'Publisher', md.publisher ) + assign( 'Imprint', md.imprint ) + assign( 'Genre', md.genre ) + assign( 'Web', md.webLink ) + assign( 'PageCount', md.pageCount ) + assign( 'LanguageISO', md.language ) + assign( 'Format', md.format ) + assign( 'AgeRating', md.maturityRating ) + if md.blackAndWhite is not None and md.blackAndWhite: + ET.SubElement(root, 'BlackAndWhite').text = "Yes" + assign( 'Manga', md.manga ) + assign( 'Characters', md.characters ) + assign( 'Teams', md.teams ) + assign( 'Locations', md.locations ) + assign( 'ScanInformation', md.scanInfo ) + + # loop and add the page entries under pages node + if len( md.pages ) > 0: + pages_node = ET.SubElement(root, 'Pages') + for page_dict in md.pages: + page_node = ET.SubElement(pages_node, 'Page') + page_node.attrib = page_dict + + # self pretty-print + self.indent(root) + + # wrap it in an ElementTree instance, and save as XML + tree = ET.ElementTree(root) + return tree + + + def convertXMLToMetadata( self, tree ): + + root = tree.getroot() + + if root.tag != 'ComicInfo': + raise 1 + return None + + metadata = GenericMetadata() + md = metadata + + + # Helper function + def xlate( tag ): + node = root.find( tag ) + if node is not None: + return node.text + else: + return None + + md.series = xlate( 'Series' ) + md.title = xlate( 'Title' ) + md.issue = xlate( 'Number' ) + md.issueCount = xlate( 'Count' ) + md.volume = xlate( 'Volume' ) + md.alternateSeries = xlate( 'AlternateSeries' ) + md.alternateNumber = xlate( 'AlternateNumber' ) + md.alternateCount = xlate( 'AlternateCount' ) + md.comments = xlate( 'Summary' ) + md.notes = xlate( 'Notes' ) + md.year = xlate( 'Year' ) + md.month = xlate( 'Month' ) + md.day = xlate( 'Day' ) + md.publisher = xlate( 'Publisher' ) + md.imprint = xlate( 'Imprint' ) + md.genre = xlate( 'Genre' ) + md.webLink = xlate( 'Web' ) + md.language = xlate( 'LanguageISO' ) + md.format = xlate( 'Format' ) + md.manga = xlate( 'Manga' ) + md.characters = xlate( 'Characters' ) + md.teams = xlate( 'Teams' ) + md.locations = xlate( 'Locations' ) + md.pageCount = xlate( 'PageCount' ) + md.scanInfo = xlate( 'ScanInformation' ) + md.storyArc = xlate( 'StoryArc' ) + md.seriesGroup = xlate( 'SeriesGroup' ) + md.maturityRating = xlate( 'AgeRating' ) + + tmp = xlate( 'BlackAndWhite' ) + md.blackAndWhite = False + if tmp is not None and tmp.lower() in [ "yes", "true", "1" ]: + md.blackAndWhite = True + # Now extract the credit info + for n in root: + if ( n.tag == 'Writer' or + n.tag == 'Penciller' or + n.tag == 'Inker' or + n.tag == 'Colorist' or + n.tag == 'Letterer' or + n.tag == 'Editor' + ): + if n.text is not None: + for name in n.text.split(','): + metadata.addCredit( name.strip(), n.tag ) + + if n.tag == 'CoverArtist': + if n.text is not None: + for name in n.text.split(','): + metadata.addCredit( name.strip(), "Cover" ) + + # parse page data now + pages_node = root.find( "Pages" ) + if pages_node is not None: + for page in pages_node: + metadata.pages.append( page.attrib ) + #print page.attrib + + metadata.isEmpty = False + + return metadata + + def writeToExternalFile( self, filename, metadata ): + + tree = self.convertMetadataToXML( self, metadata ) + #ET.dump(tree) + tree.write(filename, encoding='utf-8') + + def readFromExternalFile( self, filename ): + + tree = ET.parse( filename ) + return self.convertXMLToMetadata( tree ) + diff --git a/filenameparser.py b/filenameparser.py new file mode 100644 index 0000000..6f3aa05 --- /dev/null +++ b/filenameparser.py @@ -0,0 +1,277 @@ +""" +Functions for parsing comic info from filename + +This should probably be re-written, but, well, it mostly works! + +""" + +""" +Copyright 2012-2014 Anthony Beville + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + + +# Some portions of this code were modified from pyComicMetaThis project +# http://code.google.com/p/pycomicmetathis/ + +import re +import os +from urllib import unquote + +class FileNameParser: + + def repl(self, m): + return ' ' * len(m.group()) + + def fixSpaces( self, string, remove_dashes=True ): + if remove_dashes: + placeholders = ['[-_]',' +'] + else: + placeholders = ['[_]',' +'] + for ph in placeholders: + string = re.sub(ph, self.repl, string ) + return string #.strip() + + + def getIssueCount( self,filename, issue_end ): + + count = "" + filename = filename[issue_end:] + + # replace any name seperators with spaces + tmpstr = self.fixSpaces(filename) + found = False + + match = re.search('(?<=\sof\s)\d+(?=\s)', tmpstr, re.IGNORECASE) + if match: + count = match.group() + found = True + + if not found: + match = re.search('(?<=\(of\s)\d+(?=\))', tmpstr, re.IGNORECASE) + if match: + count = match.group() + found = True + + + count = count.lstrip("0") + + return count + + def getIssueNumber( self, filename ): + + # Returns a tuple of issue number string, and start and end indexs in the filename + # (The indexes will be used to split the string up for further parsing) + + found = False + issue = '' + start = 0 + end = 0 + + # first, look for multiple "--", this means it's formatted differently from most: + if "--" in filename: + # the pattern seems to be that anything to left of the first "--" is the series name followed by issue + filename = re.sub("--.*", self.repl, filename) + + elif "__" in filename: + # the pattern seems to be that anything to left of the first "__" is the series name followed by issue + filename = re.sub("__.*", self.repl, filename) + + filename = filename.replace("+", " ") + + # replace parenthetical phrases with spaces + filename = re.sub( "\(.*?\)", self.repl, filename) + filename = re.sub( "\[.*?\]", self.repl, filename) + + # replace any name seperators with spaces + filename = self.fixSpaces(filename) + + # remove any "of NN" phrase with spaces (problem: this could break on some titles) + filename = re.sub( "of [\d]+", self.repl, filename) + + #print u"[{0}]".format(filename) + + # we should now have a cleaned up filename version with all the words in + # the same positions as original filename + + # make a list of each word and its position + word_list = list() + for m in re.finditer("\S+", filename): + word_list.append( (m.group(0), m.start(), m.end()) ) + + # remove the first word, since it can't be the issue number + if len(word_list) > 1: + word_list = word_list[1:] + else: + #only one word?? just bail. + return issue, start, end + + # Now try to search for the likely issue number word in the list + + # first look for a word with "#" followed by digits with optional sufix + # this is almost certainly the issue number + for w in reversed(word_list): + if re.match("#[-]?(([0-9]*\.[0-9]+|[0-9]+)(\w*))", w[0]): + found = True + break + + # same as above but w/o a '#', and only look at the last word in the list + if not found: + w = word_list[-1] + if re.match("[-]?(([0-9]*\.[0-9]+|[0-9]+)(\w*))", w[0]): + found = True + + # now try to look for a # followed by any characters + if not found: + for w in reversed(word_list): + if re.match("#\S+", w[0]): + found = True + break + + if found: + issue = w[0] + start = w[1] + end = w[2] + if issue[0] == '#': + issue = issue[1:] + + return issue, start, end + + def getSeriesName(self, filename, issue_start ): + + # use the issue number string index to split the filename string + + if issue_start != 0: + filename = filename[:issue_start] + + # in case there is no issue number, remove some obvious stuff + if "--" in filename: + # the pattern seems to be that anything to left of the first "--" is the series name followed by issue + filename = re.sub("--.*", self.repl, filename) + + elif "__" in filename: + # the pattern seems to be that anything to left of the first "__" is the series name followed by issue + filename = re.sub("__.*", self.repl, filename) + + filename = filename.replace("+", " ") + tmpstr = self.fixSpaces(filename, remove_dashes=False) + + series = tmpstr + volume = "" + + #save the last word + try: + last_word = series.split()[-1] + except: + last_word = "" + + # remove any parenthetical phrases + series = re.sub( "\(.*?\)", "", series) + + # search for volume number + match = re.search('(.+)([vV]|[Vv][oO][Ll]\.?\s?)(\d+)\s*$', series) + if match: + series = match.group(1) + volume = match.group(3) + + # if a volume wasn't found, see if the last word is a year in parentheses + # since that's a common way to designate the volume + if volume == "": + #match either (YEAR), (YEAR-), or (YEAR-YEAR2) + match = re.search("(\()(\d{4})(-(\d{4}|)|)(\))", last_word) + if match: + volume = match.group(2) + + series = series.strip() + + # if we don't have an issue number (issue_start==0), look + # for hints i.e. "TPB", "one-shot", "OS", "OGN", etc that might + # be removed to help search online + if issue_start == 0: + one_shot_words = [ "tpb", "os", "one-shot", "ogn", "gn" ] + try: + last_word = series.split()[-1] + if last_word.lower() in one_shot_words: + series = series.rsplit(' ', 1)[0] + except: + pass + + return series, volume.strip() + + def getYear( self,filename, issue_end): + + filename = filename[issue_end:] + + year = "" + # look for four digit number with "(" ")" or "--" around it + match = re.search('(\(\d\d\d\d\))|(--\d\d\d\d--)', filename) + if match: + year = match.group() + # remove non-numerics + year = re.sub("[^0-9]", "", year) + return year + + def getRemainder( self, filename, year, count, issue_end ): + + #make a guess at where the the non-interesting stuff begins + remainder = "" + + if "--" in filename: + remainder = filename.split("--",1)[1] + elif "__" in filename: + remainder = filename.split("__",1)[1] + elif issue_end != 0: + remainder = filename[issue_end:] + + remainder = self.fixSpaces(remainder, remove_dashes=False) + if year != "": + remainder = remainder.replace(year,"",1) + if count != "": + remainder = remainder.replace("of "+count,"",1) + + remainder = remainder.replace("()","") + + return remainder.strip() + + def parseFilename( self, filename ): + + # remove the path + filename = os.path.basename(filename) + + # remove the extension + filename = os.path.splitext(filename)[0] + + #url decode, just in case + filename = unquote(filename) + + # sometimes archives get messed up names from too many decodings + # often url encodings will break and leave "_28" and "_29" in place + # of "(" and ")" see if there are a number of these, and replace them + if filename.count("_28") > 1 and filename.count("_29") > 1: + filename = filename.replace("_28", "(") + filename = filename.replace("_29", ")") + + self.issue, issue_start, issue_end = self.getIssueNumber(filename) + self.series, self.volume = self.getSeriesName(filename, issue_start) + self.year = self.getYear(filename, issue_end) + self.issue_count = self.getIssueCount(filename, issue_end) + self.remainder = self.getRemainder( filename, self.year, self.issue_count, issue_end ) + + if self.issue != "": + # strip off leading zeros + self.issue = self.issue.lstrip("0") + if self.issue == "": + self.issue = "0" + if self.issue[0] == ".": + self.issue = "0" + self.issue diff --git a/genericmetadata.py b/genericmetadata.py new file mode 100644 index 0000000..8e7aeaf --- /dev/null +++ b/genericmetadata.py @@ -0,0 +1,316 @@ +""" + A python class for internal metadata storage + + The goal of this class is to handle ALL the data that might come from various + tagging schemes and databases, such as ComicVine or GCD. This makes conversion + possible, however lossy it might be + +""" + +""" +Copyright 2012-2014 Anthony Beville + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +import utils + +# These page info classes are exactly the same as the CIX scheme, since it's unique +class PageType: + FrontCover = "FrontCover" + InnerCover = "InnerCover" + Roundup = "Roundup" + Story = "Story" + Advertisement = "Advertisement" + Editorial = "Editorial" + Letters = "Letters" + Preview = "Preview" + BackCover = "BackCover" + Other = "Other" + Deleted = "Deleted" + +""" +class PageInfo: + Image = 0 + Type = PageType.Story + DoublePage = False + ImageSize = 0 + Key = "" + ImageWidth = 0 + ImageHeight = 0 +""" + +class GenericMetadata: + + def __init__(self): + + self.isEmpty = True + self.tagOrigin = None + + self.series = None + self.issue = None + self.title = None + self.publisher = None + self.month = None + self.year = None + self.day = None + self.issueCount = None + self.volume = None + self.genre = None + self.language = None # 2 letter iso code + self.comments = None # use same way as Summary in CIX + + self.volumeCount = None + self.criticalRating = None + self.country = None + + self.alternateSeries = None + self.alternateNumber = None + self.alternateCount = None + self.imprint = None + self.notes = None + self.webLink = None + self.format = None + self.manga = None + self.blackAndWhite = None + self.pageCount = None + self.maturityRating = None + + self.storyArc = None + self.seriesGroup = None + self.scanInfo = None + + self.characters = None + self.teams = None + self.locations = None + + self.credits = list() + self.tags = list() + self.pages = list() + + # Some CoMet-only items + self.price = None + self.isVersionOf = None + self.rights = None + self.identifier = None + self.lastMark = None + self.coverImage = None + + def overlay( self, new_md ): + # Overlay a metadata object on this one + # that is, when the new object has non-None + # values, over-write them to this one + + def assign( cur, new ): + if new is not None: + if type(new) == str and len(new) == 0: + setattr(self, cur, None) + else: + setattr(self, cur, new) + + if not new_md.isEmpty: + self.isEmpty = False + + assign( 'series', new_md.series ) + assign( "issue", new_md.issue ) + assign( "issueCount", new_md.issueCount ) + assign( "title", new_md.title ) + assign( "publisher", new_md.publisher ) + assign( "day", new_md.day ) + assign( "month", new_md.month ) + assign( "year", new_md.year ) + assign( "volume", new_md.volume ) + assign( "volumeCount", new_md.volumeCount ) + assign( "genre", new_md.genre ) + assign( "language", new_md.language ) + assign( "country", new_md.country ) + assign( "criticalRating", new_md.criticalRating ) + assign( "alternateSeries", new_md.alternateSeries ) + assign( "alternateNumber", new_md.alternateNumber ) + assign( "alternateCount", new_md.alternateCount ) + assign( "imprint", new_md.imprint ) + assign( "webLink", new_md.webLink ) + assign( "format", new_md.format ) + assign( "manga", new_md.manga ) + assign( "blackAndWhite", new_md.blackAndWhite ) + assign( "maturityRating", new_md.maturityRating ) + assign( "storyArc", new_md.storyArc ) + assign( "seriesGroup", new_md.seriesGroup ) + assign( "scanInfo", new_md.scanInfo ) + assign( "characters", new_md.characters ) + assign( "teams", new_md.teams ) + assign( "locations", new_md.locations ) + assign( "comments", new_md.comments ) + assign( "notes", new_md.notes ) + + assign( "price", new_md.price ) + assign( "isVersionOf", new_md.isVersionOf ) + assign( "rights", new_md.rights ) + assign( "identifier", new_md.identifier ) + assign( "lastMark", new_md.lastMark ) + + self.overlayCredits( new_md.credits ) + # TODO + + # not sure if the tags and pages should broken down, or treated + # as whole lists.... + + # For now, go the easy route, where any overlay + # value wipes out the whole list + if len(new_md.tags) > 0: + assign( "tags", new_md.tags ) + + if len(new_md.pages) > 0: + assign( "pages", new_md.pages ) + + + def overlayCredits( self, new_credits ): + for c in new_credits: + if c.has_key('primary') and c['primary']: + primary = True + else: + primary = False + + # Remove credit role if person is blank + if c['person'] == "": + for r in reversed(self.credits): + if r['role'].lower() == c['role'].lower(): + self.credits.remove(r) + # otherwise, add it! + else: + self.addCredit( c['person'], c['role'], primary ) + + def setDefaultPageList( self, count ): + # generate a default page list, with the first page marked as the cover + for i in range(count): + page_dict = dict() + page_dict['Image'] = str(i) + if i == 0: + page_dict['Type'] = PageType.FrontCover + self.pages.append( page_dict ) + + def getArchivePageIndex( self, pagenum ): + # convert the displayed page number to the page index of the file in the archive + if pagenum < len( self.pages ): + return int( self.pages[pagenum]['Image'] ) + else: + return 0 + + def getCoverPageIndexList( self ): + # return a list of archive page indices of cover pages + coverlist = [] + for p in self.pages: + if 'Type' in p and p['Type'] == PageType.FrontCover: + coverlist.append( int(p['Image'])) + + if len(coverlist) == 0: + coverlist.append( 0 ) + + return coverlist + + def addCredit( self, person, role, primary = False ): + + credit = dict() + credit['person'] = person + credit['role'] = role + if primary: + credit['primary'] = primary + + # look to see if it's not already there... + found = False + for c in self.credits: + if ( c['person'].lower() == person.lower() and + c['role'].lower() == role.lower() ): + # no need to add it. just adjust the "primary" flag as needed + c['primary'] = primary + found = True + break + + if not found: + self.credits.append(credit) + + + def __str__( self ): + vals = [] + if self.isEmpty: + return "No metadata" + + def add_string( tag, val ): + if val is not None and u"{0}".format(val) != "": + vals.append( (tag, val) ) + + def add_attr_string( tag ): + val = getattr(self,tag) + add_string( tag, getattr(self,tag) ) + + add_attr_string( "series" ) + add_attr_string( "issue" ) + add_attr_string( "issueCount" ) + add_attr_string( "title" ) + add_attr_string( "publisher" ) + add_attr_string( "year" ) + add_attr_string( "month" ) + add_attr_string( "day" ) + add_attr_string( "volume" ) + add_attr_string( "volumeCount" ) + add_attr_string( "genre" ) + add_attr_string( "language" ) + add_attr_string( "country" ) + add_attr_string( "criticalRating" ) + add_attr_string( "alternateSeries" ) + add_attr_string( "alternateNumber" ) + add_attr_string( "alternateCount" ) + add_attr_string( "imprint" ) + add_attr_string( "webLink" ) + add_attr_string( "format" ) + add_attr_string( "manga" ) + + add_attr_string( "price" ) + add_attr_string( "isVersionOf" ) + add_attr_string( "rights" ) + add_attr_string( "identifier" ) + add_attr_string( "lastMark" ) + + if self.blackAndWhite: + add_attr_string( "blackAndWhite" ) + add_attr_string( "maturityRating" ) + add_attr_string( "storyArc" ) + add_attr_string( "seriesGroup" ) + add_attr_string( "scanInfo" ) + add_attr_string( "characters" ) + add_attr_string( "teams" ) + add_attr_string( "locations" ) + add_attr_string( "comments" ) + add_attr_string( "notes" ) + + add_string( "tags", utils.listToString( self.tags ) ) + + for c in self.credits: + primary = "" + if c.has_key('primary') and c['primary']: + primary = " [P]" + add_string( "credit", c['role']+": "+c['person'] + primary) + + # find the longest field name + flen = 0 + for i in vals: + flen = max( flen, len(i[0]) ) + flen += 1 + + #format the data nicely + outstr = "" + fmt_str = u"{0: <" + str(flen) + "} {1}\n" + for i in vals: + outstr += fmt_str.format( i[0]+":", i[1] ) + + return outstr diff --git a/issuestring.py b/issuestring.py new file mode 100644 index 0000000..751aa8c --- /dev/null +++ b/issuestring.py @@ -0,0 +1,140 @@ +# coding=utf-8 +""" +Class for handling the odd permutations of an 'issue number' that the comics industry throws at us + +e.g.: + +"12" +"12.1" +"0" +"-1" +"5AU" +"100-2" + +""" + +""" +Copyright 2012-2014 Anthony Beville + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +import utils +import math +import re + +class IssueString: + def __init__(self, text): + + # break up the issue number string into 2 parts: the numeric and suffix string. + # ( assumes that the numeric portion is always first ) + + self.num = None + self.suffix = "" + + if text is None: + return + + if type(text) == int: + text = str(text) + + if len(text) == 0: + return + + text = unicode(text) + + #skip the minus sign if it's first + if text[0] == '-': + start = 1 + else: + start = 0 + + # if it's still not numeric at start skip it + if text[start].isdigit() or text[start] == ".": + # walk through the string, look for split point (the first non-numeric) + decimal_count = 0 + for idx in range( start, len(text) ): + if text[idx] not in "0123456789.": + break + # special case: also split on second "." + if text[idx] == ".": + decimal_count += 1 + if decimal_count > 1: + break + else: + idx = len(text) + + # move trailing numeric decimal to suffix + # (only if there is other junk after ) + if text[idx-1] == "." and len(text) != idx: + idx = idx -1 + + # if there is no numeric after the minus, make the minus part of the suffix + if idx == 1 and start == 1: + idx = 0 + + part1 = text[0:idx] + part2 = text[idx:len(text)] + + if part1 != "": + self.num = float( part1 ) + self.suffix = part2 + else: + self.suffix = text + + #print "num: {0} suf: {1}".format(self.num, self.suffix) + + def asString( self, pad = 0 ): + #return the float, left side zero-padded, with suffix attached + if self.num is None: + return self.suffix + + negative = self.num < 0 + + num_f = abs(self.num) + + num_int = int( num_f ) + num_s = str( num_int ) + if float( num_int ) != num_f: + num_s = str( num_f ) + + num_s += self.suffix + + # create padding + padding = "" + l = len( str(num_int)) + if l < pad : + padding = "0" * (pad - l) + + num_s = padding + num_s + if negative: + num_s = "-" + num_s + + return num_s + + def asFloat( self ): + #return the float, with no suffix + if self.suffix == u"½": + if self.num is not None: + return self.num + .5 + else: + return .5 + return self.num + + def asInt( self ): + #return the int version of the float + if self.num is None: + return None + return int( self.num ) + + diff --git a/utils.py b/utils.py new file mode 100644 index 0000000..e315cd7 --- /dev/null +++ b/utils.py @@ -0,0 +1,597 @@ +# coding=utf-8 + +""" +Some generic utilities +""" + + +""" +Copyright 2012-2014 Anthony Beville + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" +import sys +import os +import re +import platform +import locale +import codecs + + +class UtilsVars: + already_fixed_encoding = False + +def get_actual_preferred_encoding(): + preferred_encoding = locale.getpreferredencoding() + if platform.system() == "Darwin": + preferred_encoding = "utf-8" + return preferred_encoding + +def fix_output_encoding( ): + if not UtilsVars.already_fixed_encoding: + # this reads the environment and inits the right locale + locale.setlocale(locale.LC_ALL, "") + + # try to make stdout/stderr encodings happy for unicode printing + preferred_encoding = get_actual_preferred_encoding() + sys.stdout = codecs.getwriter(preferred_encoding)(sys.stdout) + sys.stderr = codecs.getwriter(preferred_encoding)(sys.stderr) + UtilsVars.already_fixed_encoding = True + +def get_recursive_filelist( pathlist ): + """ + Get a recursive list of of all files under all path items in the list + """ + filename_encoding = sys.getfilesystemencoding() + filelist = [] + for p in pathlist: + # if path is a folder, walk it recursivly, and all files underneath + if type(p) == str: + #make sure string is unicode + p = p.decode(filename_encoding) #, 'replace') + elif type(p) != unicode: + #it's probably a QString + p = unicode(p) + + if os.path.isdir( p ): + for root,dirs,files in os.walk( p ): + for f in files: + if type(f) == str: + #make sure string is unicode + f = f.decode(filename_encoding, 'replace') + elif type(f) != unicode: + #it's probably a QString + f = unicode(f) + filelist.append(os.path.join(root,f)) + else: + filelist.append(p) + + return filelist + +def listToString( l ): + string = "" + if l is not None: + for item in l: + if len(string) > 0: + string += ", " + string += item + return string + +def addtopath( dirname ): + if dirname is not None and dirname != "": + + # verify that path doesn't already contain the given dirname + tmpdirname = re.escape(dirname) + pattern = r"{sep}{dir}$|^{dir}{sep}|{sep}{dir}{sep}|^{dir}$".format( dir=tmpdirname, sep=os.pathsep) + + match = re.search(pattern, os.environ['PATH']) + if not match: + os.environ['PATH'] = dirname + os.pathsep + os.environ['PATH'] + +# returns executable path, if it exists +def which(program): + + def is_exe(fpath): + return os.path.isfile(fpath) and os.access(fpath, os.X_OK) + + fpath, fname = os.path.split(program) + if fpath: + if is_exe(program): + return program + else: + for path in os.environ["PATH"].split(os.pathsep): + exe_file = os.path.join(path, program) + if is_exe(exe_file): + return exe_file + + return None + +def removearticles( text ): + text = text.lower() + articles = ['and', 'the', 'a', '&', 'issue' ] + newText = '' + for word in text.split(' '): + if word not in articles: + newText += word+' ' + + newText = newText[:-1] + + # now get rid of some other junk + newText = newText.replace(":", "") + newText = newText.replace(",", "") + newText = newText.replace("-", " ") + + # since the CV api changed, searches for series names with periods + # now explicity require the period to be in the search key, + # so the line below is removed (for now) + #newText = newText.replace(".", "") + + return newText + + +def unique_file(file_name): + counter = 1 + file_name_parts = os.path.splitext(file_name) # returns ('/path/file', '.ext') + while 1: + if not os.path.lexists( file_name): + return file_name + file_name = file_name_parts[0] + ' (' + str(counter) + ')' + file_name_parts[1] + counter += 1 + + +# -o- coding: utf-8 -o- +# ISO639 python dict +# oficial list in http://www.loc.gov/standards/iso639-2/php/code_list.php + +lang_dict = { + 'ab': 'Abkhaz', + 'aa': 'Afar', + 'af': 'Afrikaans', + 'ak': 'Akan', + 'sq': 'Albanian', + 'am': 'Amharic', + 'ar': 'Arabic', + 'an': 'Aragonese', + 'hy': 'Armenian', + 'as': 'Assamese', + 'av': 'Avaric', + 'ae': 'Avestan', + 'ay': 'Aymara', + 'az': 'Azerbaijani', + 'bm': 'Bambara', + 'ba': 'Bashkir', + 'eu': 'Basque', + 'be': 'Belarusian', + 'bn': 'Bengali', + 'bh': 'Bihari', + 'bi': 'Bislama', + 'bs': 'Bosnian', + 'br': 'Breton', + 'bg': 'Bulgarian', + 'my': 'Burmese', + 'ca': 'Catalan; Valencian', + 'ch': 'Chamorro', + 'ce': 'Chechen', + 'ny': 'Chichewa; Chewa; Nyanja', + 'zh': 'Chinese', + 'cv': 'Chuvash', + 'kw': 'Cornish', + 'co': 'Corsican', + 'cr': 'Cree', + 'hr': 'Croatian', + 'cs': 'Czech', + 'da': 'Danish', + 'dv': 'Divehi; Maldivian;', + 'nl': 'Dutch', + 'dz': 'Dzongkha', + 'en': 'English', + 'eo': 'Esperanto', + 'et': 'Estonian', + 'ee': 'Ewe', + 'fo': 'Faroese', + 'fj': 'Fijian', + 'fi': 'Finnish', + 'fr': 'French', + 'ff': 'Fula', + 'gl': 'Galician', + 'ka': 'Georgian', + 'de': 'German', + 'el': 'Greek, Modern', + 'gn': 'Guaraní', + 'gu': 'Gujarati', + 'ht': 'Haitian', + 'ha': 'Hausa', + 'he': 'Hebrew (modern)', + 'hz': 'Herero', + 'hi': 'Hindi', + 'ho': 'Hiri Motu', + 'hu': 'Hungarian', + 'ia': 'Interlingua', + 'id': 'Indonesian', + 'ie': 'Interlingue', + 'ga': 'Irish', + 'ig': 'Igbo', + 'ik': 'Inupiaq', + 'io': 'Ido', + 'is': 'Icelandic', + 'it': 'Italian', + 'iu': 'Inuktitut', + 'ja': 'Japanese', + 'jv': 'Javanese', + 'kl': 'Kalaallisut', + 'kn': 'Kannada', + 'kr': 'Kanuri', + 'ks': 'Kashmiri', + 'kk': 'Kazakh', + 'km': 'Khmer', + 'ki': 'Kikuyu, Gikuyu', + 'rw': 'Kinyarwanda', + 'ky': 'Kirghiz, Kyrgyz', + 'kv': 'Komi', + 'kg': 'Kongo', + 'ko': 'Korean', + 'ku': 'Kurdish', + 'kj': 'Kwanyama, Kuanyama', + 'la': 'Latin', + 'lb': 'Luxembourgish', + 'lg': 'Luganda', + 'li': 'Limburgish', + 'ln': 'Lingala', + 'lo': 'Lao', + 'lt': 'Lithuanian', + 'lu': 'Luba-Katanga', + 'lv': 'Latvian', + 'gv': 'Manx', + 'mk': 'Macedonian', + 'mg': 'Malagasy', + 'ms': 'Malay', + 'ml': 'Malayalam', + 'mt': 'Maltese', + 'mi': 'Māori', + 'mr': 'Marathi (Marāṭhī)', + 'mh': 'Marshallese', + 'mn': 'Mongolian', + 'na': 'Nauru', + 'nv': 'Navajo, Navaho', + 'nb': 'Norwegian Bokmål', + 'nd': 'North Ndebele', + 'ne': 'Nepali', + 'ng': 'Ndonga', + 'nn': 'Norwegian Nynorsk', + 'no': 'Norwegian', + 'ii': 'Nuosu', + 'nr': 'South Ndebele', + 'oc': 'Occitan', + 'oj': 'Ojibwe, Ojibwa', + 'cu': 'Old Church Slavonic', + 'om': 'Oromo', + 'or': 'Oriya', + 'os': 'Ossetian, Ossetic', + 'pa': 'Panjabi, Punjabi', + 'pi': 'Pāli', + 'fa': 'Persian', + 'pl': 'Polish', + 'ps': 'Pashto, Pushto', + 'pt': 'Portuguese', + 'qu': 'Quechua', + 'rm': 'Romansh', + 'rn': 'Kirundi', + 'ro': 'Romanian, Moldavan', + 'ru': 'Russian', + 'sa': 'Sanskrit (Saṁskṛta)', + 'sc': 'Sardinian', + 'sd': 'Sindhi', + 'se': 'Northern Sami', + 'sm': 'Samoan', + 'sg': 'Sango', + 'sr': 'Serbian', + 'gd': 'Scottish Gaelic', + 'sn': 'Shona', + 'si': 'Sinhala, Sinhalese', + 'sk': 'Slovak', + 'sl': 'Slovene', + 'so': 'Somali', + 'st': 'Southern Sotho', + 'es': 'Spanish; Castilian', + 'su': 'Sundanese', + 'sw': 'Swahili', + 'ss': 'Swati', + 'sv': 'Swedish', + 'ta': 'Tamil', + 'te': 'Telugu', + 'tg': 'Tajik', + 'th': 'Thai', + 'ti': 'Tigrinya', + 'bo': 'Tibetan', + 'tk': 'Turkmen', + 'tl': 'Tagalog', + 'tn': 'Tswana', + 'to': 'Tonga', + 'tr': 'Turkish', + 'ts': 'Tsonga', + 'tt': 'Tatar', + 'tw': 'Twi', + 'ty': 'Tahitian', + 'ug': 'Uighur, Uyghur', + 'uk': 'Ukrainian', + 'ur': 'Urdu', + 'uz': 'Uzbek', + 've': 'Venda', + 'vi': 'Vietnamese', + 'vo': 'Volapük', + 'wa': 'Walloon', + 'cy': 'Welsh', + 'wo': 'Wolof', + 'fy': 'Western Frisian', + 'xh': 'Xhosa', + 'yi': 'Yiddish', + 'yo': 'Yoruba', + 'za': 'Zhuang, Chuang', + 'zu': 'Zulu', +} + + +countries = [ + ('AF', 'Afghanistan'), + ('AL', 'Albania'), + ('DZ', 'Algeria'), + ('AS', 'American Samoa'), + ('AD', 'Andorra'), + ('AO', 'Angola'), + ('AI', 'Anguilla'), + ('AQ', 'Antarctica'), + ('AG', 'Antigua And Barbuda'), + ('AR', 'Argentina'), + ('AM', 'Armenia'), + ('AW', 'Aruba'), + ('AU', 'Australia'), + ('AT', 'Austria'), + ('AZ', 'Azerbaijan'), + ('BS', 'Bahamas'), + ('BH', 'Bahrain'), + ('BD', 'Bangladesh'), + ('BB', 'Barbados'), + ('BY', 'Belarus'), + ('BE', 'Belgium'), + ('BZ', 'Belize'), + ('BJ', 'Benin'), + ('BM', 'Bermuda'), + ('BT', 'Bhutan'), + ('BO', 'Bolivia'), + ('BA', 'Bosnia And Herzegowina'), + ('BW', 'Botswana'), + ('BV', 'Bouvet Island'), + ('BR', 'Brazil'), + ('BN', 'Brunei Darussalam'), + ('BG', 'Bulgaria'), + ('BF', 'Burkina Faso'), + ('BI', 'Burundi'), + ('KH', 'Cambodia'), + ('CM', 'Cameroon'), + ('CA', 'Canada'), + ('CV', 'Cape Verde'), + ('KY', 'Cayman Islands'), + ('CF', 'Central African Rep'), + ('TD', 'Chad'), + ('CL', 'Chile'), + ('CN', 'China'), + ('CX', 'Christmas Island'), + ('CC', 'Cocos Islands'), + ('CO', 'Colombia'), + ('KM', 'Comoros'), + ('CG', 'Congo'), + ('CK', 'Cook Islands'), + ('CR', 'Costa Rica'), + ('CI', 'Cote D`ivoire'), + ('HR', 'Croatia'), + ('CU', 'Cuba'), + ('CY', 'Cyprus'), + ('CZ', 'Czech Republic'), + ('DK', 'Denmark'), + ('DJ', 'Djibouti'), + ('DM', 'Dominica'), + ('DO', 'Dominican Republic'), + ('TP', 'East Timor'), + ('EC', 'Ecuador'), + ('EG', 'Egypt'), + ('SV', 'El Salvador'), + ('GQ', 'Equatorial Guinea'), + ('ER', 'Eritrea'), + ('EE', 'Estonia'), + ('ET', 'Ethiopia'), + ('FK', 'Falkland Islands (Malvinas)'), + ('FO', 'Faroe Islands'), + ('FJ', 'Fiji'), + ('FI', 'Finland'), + ('FR', 'France'), + ('GF', 'French Guiana'), + ('PF', 'French Polynesia'), + ('TF', 'French S. Territories'), + ('GA', 'Gabon'), + ('GM', 'Gambia'), + ('GE', 'Georgia'), + ('DE', 'Germany'), + ('GH', 'Ghana'), + ('GI', 'Gibraltar'), + ('GR', 'Greece'), + ('GL', 'Greenland'), + ('GD', 'Grenada'), + ('GP', 'Guadeloupe'), + ('GU', 'Guam'), + ('GT', 'Guatemala'), + ('GN', 'Guinea'), + ('GW', 'Guinea-bissau'), + ('GY', 'Guyana'), + ('HT', 'Haiti'), + ('HN', 'Honduras'), + ('HK', 'Hong Kong'), + ('HU', 'Hungary'), + ('IS', 'Iceland'), + ('IN', 'India'), + ('ID', 'Indonesia'), + ('IR', 'Iran'), + ('IQ', 'Iraq'), + ('IE', 'Ireland'), + ('IL', 'Israel'), + ('IT', 'Italy'), + ('JM', 'Jamaica'), + ('JP', 'Japan'), + ('JO', 'Jordan'), + ('KZ', 'Kazakhstan'), + ('KE', 'Kenya'), + ('KI', 'Kiribati'), + ('KP', 'Korea (North)'), + ('KR', 'Korea (South)'), + ('KW', 'Kuwait'), + ('KG', 'Kyrgyzstan'), + ('LA', 'Laos'), + ('LV', 'Latvia'), + ('LB', 'Lebanon'), + ('LS', 'Lesotho'), + ('LR', 'Liberia'), + ('LY', 'Libya'), + ('LI', 'Liechtenstein'), + ('LT', 'Lithuania'), + ('LU', 'Luxembourg'), + ('MO', 'Macau'), + ('MK', 'Macedonia'), + ('MG', 'Madagascar'), + ('MW', 'Malawi'), + ('MY', 'Malaysia'), + ('MV', 'Maldives'), + ('ML', 'Mali'), + ('MT', 'Malta'), + ('MH', 'Marshall Islands'), + ('MQ', 'Martinique'), + ('MR', 'Mauritania'), + ('MU', 'Mauritius'), + ('YT', 'Mayotte'), + ('MX', 'Mexico'), + ('FM', 'Micronesia'), + ('MD', 'Moldova'), + ('MC', 'Monaco'), + ('MN', 'Mongolia'), + ('MS', 'Montserrat'), + ('MA', 'Morocco'), + ('MZ', 'Mozambique'), + ('MM', 'Myanmar'), + ('NA', 'Namibia'), + ('NR', 'Nauru'), + ('NP', 'Nepal'), + ('NL', 'Netherlands'), + ('AN', 'Netherlands Antilles'), + ('NC', 'New Caledonia'), + ('NZ', 'New Zealand'), + ('NI', 'Nicaragua'), + ('NE', 'Niger'), + ('NG', 'Nigeria'), + ('NU', 'Niue'), + ('NF', 'Norfolk Island'), + ('MP', 'Northern Mariana Islands'), + ('NO', 'Norway'), + ('OM', 'Oman'), + ('PK', 'Pakistan'), + ('PW', 'Palau'), + ('PA', 'Panama'), + ('PG', 'Papua New Guinea'), + ('PY', 'Paraguay'), + ('PE', 'Peru'), + ('PH', 'Philippines'), + ('PN', 'Pitcairn'), + ('PL', 'Poland'), + ('PT', 'Portugal'), + ('PR', 'Puerto Rico'), + ('QA', 'Qatar'), + ('RE', 'Reunion'), + ('RO', 'Romania'), + ('RU', 'Russian Federation'), + ('RW', 'Rwanda'), + ('KN', 'Saint Kitts And Nevis'), + ('LC', 'Saint Lucia'), + ('VC', 'St Vincent/Grenadines'), + ('WS', 'Samoa'), + ('SM', 'San Marino'), + ('ST', 'Sao Tome'), + ('SA', 'Saudi Arabia'), + ('SN', 'Senegal'), + ('SC', 'Seychelles'), + ('SL', 'Sierra Leone'), + ('SG', 'Singapore'), + ('SK', 'Slovakia'), + ('SI', 'Slovenia'), + ('SB', 'Solomon Islands'), + ('SO', 'Somalia'), + ('ZA', 'South Africa'), + ('ES', 'Spain'), + ('LK', 'Sri Lanka'), + ('SH', 'St. Helena'), + ('PM', 'St.Pierre'), + ('SD', 'Sudan'), + ('SR', 'Suriname'), + ('SZ', 'Swaziland'), + ('SE', 'Sweden'), + ('CH', 'Switzerland'), + ('SY', 'Syrian Arab Republic'), + ('TW', 'Taiwan'), + ('TJ', 'Tajikistan'), + ('TZ', 'Tanzania'), + ('TH', 'Thailand'), + ('TG', 'Togo'), + ('TK', 'Tokelau'), + ('TO', 'Tonga'), + ('TT', 'Trinidad And Tobago'), + ('TN', 'Tunisia'), + ('TR', 'Turkey'), + ('TM', 'Turkmenistan'), + ('TV', 'Tuvalu'), + ('UG', 'Uganda'), + ('UA', 'Ukraine'), + ('AE', 'United Arab Emirates'), + ('UK', 'United Kingdom'), + ('US', 'United States'), + ('UY', 'Uruguay'), + ('UZ', 'Uzbekistan'), + ('VU', 'Vanuatu'), + ('VA', 'Vatican City State'), + ('VE', 'Venezuela'), + ('VN', 'Viet Nam'), + ('VG', 'Virgin Islands (British)'), + ('VI', 'Virgin Islands (U.S.)'), + ('EH', 'Western Sahara'), + ('YE', 'Yemen'), + ('YU', 'Yugoslavia'), + ('ZR', 'Zaire'), + ('ZM', 'Zambia'), + ('ZW', 'Zimbabwe') +] + + + +def getLanguageDict(): + return lang_dict + +def getLanguageFromISO( iso ): + if iso == None: + return None + else: + return lang_dict[ iso ] + + + + + + + + + +