ajout de la generation du package plasma comic

This commit is contained in:
Forjan Frederich 2009-05-07 20:49:14 +00:00
commit dcabb39d47
3 changed files with 853 additions and 0 deletions

74
plasma-comic/generate.php Executable file
View file

@ -0,0 +1,74 @@
<?php
/**
* Strip-It Plasma Comic package generator
*
* @author Frédéric 'GeoVah' Forjan
* @license http://www.gnu.org/licenses/gpl.html GPL
* @copyright 2009 Forjan Frédéric
*
* @package stripit
*/
set_include_path(get_include_path() . PATH_SEPARATOR . getcwd());
(@include_once '../conf/configuration.php') or
die("Strip It isn't configured yet: conf/configuration.php file is missing.<br/>See README for details.");
@require_once("zipstream.php");
/**
* generate the metadata conf
*/
function getMetadataContent($conf)
{
return "[Desktop Entry]\n".
"Name=". $conf->title ."\n" .
"Comment=". $conf->title . "\n" .
"Type=Service\n" .
"X-KDE-ServiceTypes=Plasma/Comic\n" .
"Icon=favicon.png\n" .
"\n" .
"X-KDE-Library=plasma_comic_krossprovider\n" .
"X-KDE-PluginInfo-Author=Frederic Forjan\n" .
"X-KDE-PluginInfo-Email=fforjab@free.fr\n" .
"X-KDE-PluginInfo-Name=".$conf->title."\n" .
"X-KDE-PluginInfo-Version=0.2\n" .
"X-KDE-PluginInfo-Website=http://stripit.sf.net\n" .
"X-KDE-PluginInfo-License=GPLv2\n" .
"X-KDE-PluginInfo-EnabledByDefault=true\n" .
"X-KDE-PlasmaComicProvider-SuffixType=Number";
}
/**
* return the favicon content
*/
function getFaviconContent()
{
return implode("",file("../favicon.png"));
}
/**
* generate the main.es file
*/
function getMainEsContent($conf)
{
return "//auto-generated URL\nvar URL = \"" . $conf->url ."/\"\n" . implode("",file("main.es.template"));
}
# create a new stream object
$conf = new configuration();
$zipfile = new ZipStream($conf->title .".comic",array('content_type'=>'application/octet-stream'));
$conf = new configuration();
$zipfile -> add_file("favicon.png",getFaviconContent());
$zipfile -> add_file("metadata.desktop",getMetadataContent($conf));
$zipfile -> add_file("contents/code/main.es",getMainEsContent($conf));
$zipfile -> finish();
?>

View file

@ -0,0 +1,198 @@
/*
* Copyright (C) 2007 Frédéric Forjan <fforjan@free.fr>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Library General Public License version 2 as
* published by the Free Software Foundation
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
function websiteUrl(id)
{
if(id ==0)
return URL + "index.php";
return URL + "index.php?strip=" + (id-1);
}
//init function
function init()
{
print("** Init() **");
//we get the current ID, 0 if its first time !
var currentID = comic.identifier;
print("current ID :"+ currentID);
//our first id is always 1...
comic.firstIdentifier = 1;
//so if we are on ID 0, first time so go to take the latest image !
if (currentID == 0) {
print("loading the latest image...");
comic.requestPage(websiteUrl(0), comic.User);
}
else
{
comic.requestPage(websiteUrl(currentID), comic.Page);
}
}
function getTitle(data)
{
var title = "";
var reTitle = new RegExp('<title>.* - (.+)</title>');
var match = reTitle.exec(data);
if ( match != null ) {
title = match[1];
print("title :" +title);
} else {
print("title not found !");
}
return title;
}
function getLastID(data)
{
var lastID = 0;
//we retrieve the last ID from nav link...
var re = new RegExp('id="navlast" href="index\\.php\\?strip=(\\d+)"');
var match = re.exec(data);
if ( match != null ) {
lastID = match[1];
print("last id = " +lastID);
}
else {
print("cannot parse last id");
}
return parseInt(lastID)+1;
}
function getNextID(data)
{
var nextID = 0;
//we retrieve the next ID from nav link...
var re = new RegExp('id="navnext" href="index\\.php\\?strip=(\\d+)"');
var match = re.exec(data);
if ( match != null ) {
nextID = match[1];
print("next id = " +nextID);
}
else {
print("cannot parse next id");
}
return parseInt(nextID)+1;
}
function getPrevID(data)
{
var prevID = 0;
//we retrieve the prev ID from nav link...
var re = new RegExp('id="navprev" href="index\\.php\\?strip=(\\d+)"');
var match = re.exec(data);
if ( match != null ) {
prevID = match[1];
print("prev id = " +prevID);
}
else {
print("cannot parse prev id");
}
return parseInt(prevID)+1;
}
function getAdditionnalText(data)
{
/** @FF remove the additionnal text since we cannot escape html char..today ?
var reAddText = new RegExp('<img id="strip" class="content" src="\./strips/.*png" alt="(.+)" />');
match = reAddText.exec(data);
if ( match != null ) {
var addText = match[1];
comic.additionalText = addText;
print("addText :" +addText);
} else {
print("addText not found !");
}
*/
}
function getAuthor(data)
{
var author ="<unknown>";
//we retrieve the last ID from nav link...
var re = new RegExp('<p id="author">(.+)</p>.+<p id="date">');
var match = re.exec(data);
if ( match != null ) {
author = match[1];
print("author = " +author);
}
else {
print("cannot parse last id");
}
return author;
}
function getImageUrl(data)
{
var url ="<unknown>";
//we retrieve the last ID from nav link...
var re = new RegExp('<img id="strip" class="content" src="(.+)" alt');
var match = re.exec(data);
if ( match != null ) {
url = URL + match[1];
print("img url = " +url);
}
else {
print("cannot parse image url");
}
return url;
}
function pageRetrieved(id, data)
{
if (id == comic.User) {
//find the most recent strip
var lastid = getLastID(data);
comic.identifier = lastid;
comic.requestPage(websiteUrl(lastid), comic.Page);
}
if( id == comic.Page) {
print("parsing page for id :" + comic.identifier);
//set the current url
comic.websiteUrl = websiteUrl(comic.identifier);
//retrieve the title
comic.title = getTitle(data);
//retrieve the last ID
comic.lastIdentifier = getLastID(data);
comic.nextIdentifier = getNextID(data);
comic.previousIdentifier = getPrevID(data);
//retrieve the author
comic.comicAuthor = getAuthor(data);
//load the image
comic.requestPage(getImageUrl(data),comic.Image);
}
}

581
plasma-comic/zipstream.php Normal file
View file

@ -0,0 +1,581 @@
<?php
##########################################################################
# ZipStream - Streamed, dynamically generated zip archives. #
# by Paul Duncan <pabs@pablotron.org> #
# #
# Copyright (C) 2007-2009 Paul Duncan <pabs@pablotron.org> #
# #
# 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 of the Software, its documentation and #
# marketing & publicity materials, and acknowledgment shall be given in #
# the documentation, materials and software packages that this Software #
# was used. #
# #
# 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 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. #
##########################################################################
#
# ZipStream - Streamed, dynamically generated zip archives.
# by Paul Duncan <pabs@pablotron.org>
#
# Requirements:
#
# * PHP version 5.1.2 or newer.
#
# Usage:
#
# Streaming zip archives is a simple, three-step process:
#
# 1. Create the zip stream:
#
# $zip = new ZipStream('example.zip');
#
# 2. Add one or more files to the archive:
#
# # add first file
# $data = file_get_contents('some_file.gif');
# $zip->add_file('some_file.gif', $data);
#
# # add second file
# $data = file_get_contents('some_file.gif');
# $zip->add_file('another_file.png', $data);
#
# 3. Finish the zip stream:
#
# $zip->finish();
#
# You can also add an archive comment, add comments to individual files,
# and adjust the timestamp of files. See the API documentation for each
# method below for additional information.
#
# Example:
#
# # create a new zip stream object
# $zip = new ZipStream('some_files.zip');
#
# # list of local files
# $files = array('foo.txt', 'bar.jpg');
#
# # read and add each file to the archive
# foreach ($files as $path)
# $zip->add_file($path, file_get_contents($path));
#
# # write archive footer to stream
# $zip->finish();
#
class ZipStream {
const VERSION = '0.2.1';
var $opt = array(),
$files = array(),
$cdr_ofs = 0,
$ofs = 0;
#
# Create a new ZipStream object.
#
# Parameters:
#
# $name - Name of output file (optional).
# $opt - Hash of archive options (optional, see "Archive Options"
# below).
#
# Archive Options:
#
# comment - Comment for this archive.
# content_type - HTTP Content-Type. Defaults to 'application/x-zip'.
# content_disposition - HTTP Content-Disposition. Defaults to
# 'attachment; filename=\"FILENAME\"', where
# FILENAME is the specified filename.
# large_file_size - Size, in bytes, of the largest file to try
# and load into memory (used by
# add_file_from_path()). Large files may also
# be compressed differently; see the
# 'large_file_method' option.
# large_file_method - How to handle large files. Legal values are
# 'store' (the default), or 'deflate'. Store
# sends the file raw and is significantly
# faster, while 'deflate' compresses the file
# and is much, much slower. Note that deflate
# must compress the file twice and extremely
# slow.
# send_http_headers - Boolean indicating whether or not to send
# the HTTP headers for this file.
#
# Note that content_type and content_disposition do nothing if you are
# not sending HTTP headers.
#
# Large File Support:
#
# By default, the method add_file_from_path() will send send files
# larger than 20 megabytes along raw rather than attempting to
# compress them. You can change both the maximum size and the
# compression behavior using the large_file_* options above, with the
# following caveats:
#
# * For "small" files (e.g. files smaller than large_file_size), the
# memory use can be up to twice that of the actual file. In other
# words, adding a 10 megabyte file to the archive could potentially
# occupty 20 megabytes of memory.
#
# * Enabling compression on large files (e.g. files larger than
# large_file_size) is extremely slow, because ZipStream has to pass
# over the large file once to calculate header information, and then
# again to compress and send the actual data.
#
# Examples:
#
# # create a new zip file named 'foo.zip'
# $zip = new ZipStream('foo.zip');
#
# # create a new zip file named 'bar.zip' with a comment
# $zip = new ZipStream('bar.zip', array(
# 'comment' => 'this is a comment for the zip file.',
# ));
#
# Notes:
#
# If you do not set a filename, then this library _DOES NOT_ send HTTP
# headers by default. This behavior is to allow software to send its
# own headers (including the filename), and still use this library.
#
function __construct($name = null, $opt = array()) {
# save options
$this->opt = $opt;
# set large file defaults: size = 20 megabytes, method = store
if (!$this->opt['large_file_size'])
$this->opt['large_file_size'] = 20 * 1024 * 1024;
if (!$this->opt['large_file_method'])
$this->opt['large_file_method'] = 'store';
$this->output_name = $name;
if ($name || $opt['send_http_headers'])
$this->need_headers = true;
}
#
# add_file - add a file to the archive
#
# Parameters:
#
# $name - path of file in archive (including directory).
# $data - contents of file
# $opt - Hash of options for file (optional, see "File Options"
# below).
#
# File Options:
# time - Last-modified timestamp (seconds since the epoch) of
# this file. Defaults to the current time.
# comment - Comment related to this file.
#
# Examples:
#
# # add a file named 'foo.txt'
# $data = file_get_contents('foo.txt');
# $zip->add_file('foo.txt', $data);
#
# # add a file named 'bar.jpg' with a comment and a last-modified
# # time of two hours ago
# $data = file_get_contents('bar.jpg');
# $zip->add_file('bar.jpg', $data, array(
# 'time' => time() - 2 * 3600,
# 'comment' => 'this is a comment about bar.jpg',
# ));
#
function add_file($name, $data, $opt = array()) {
# compress data
$zdata = gzdeflate($data);
# calculate header attributes
$crc = crc32($data);
$zlen = strlen($zdata);
$len = strlen($data);
$meth = 0x08;
# send file header
$this->add_file_header($name, $opt, $meth, $crc, $zlen, $len);
# print data
$this->send($zdata);
}
#
# add_file_from_path - add a file at path to the archive.
#
# Note that large files may be compresed differently than smaller
# files; see the "Large File Support" section above for more
# information.
#
# Parameters:
#
# $name - name of file in archive (including directory path).
# $path - path to file on disk (note: paths should be encoded using
# UNIX-style forward slashes -- e.g '/path/to/some/file').
# $opt - Hash of options for file (optional, see "File Options"
# below).
#
# File Options:
# time - Last-modified timestamp (seconds since the epoch) of
# this file. Defaults to the current time.
# comment - Comment related to this file.
#
# Examples:
#
# # add a file named 'foo.txt' from the local file '/tmp/foo.txt'
# $zip->add_file_from_path('foo.txt', '/tmp/foo.txt');
#
# # add a file named 'bigfile.rar' from the local file
# # '/usr/share/bigfile.rar' with a comment and a last-modified
# # time of two hours ago
# $path = '/usr/share/bigfile.rar';
# $zip->add_file_from_path('bigfile.rar', $path, array(
# 'time' => time() - 2 * 3600,
# 'comment' => 'this is a comment about bar.jpg',
# ));
#
function add_file_from_path($name, $path, $opt = array()) {
if ($this->is_large_file($path)) {
# file is too large to be read into memory; add progressively
$this->add_large_file($name, $path, $opt);
} else {
# file is small enough to read into memory; read file contents and
# handle with add_file()
$data = file_get_contents($path);
$this->add_file($name, $data, $opt);
}
}
#
# finish - Write zip footer to stream.
#
# Example:
#
# # add a list of files to the archive
# $files = array('foo.txt', 'bar.jpg');
# foreach ($files as $path)
# $zip->add_file($path, file_get_contents($path));
#
# # write footer to stream
# $zip->finish();
#
function finish() {
# add trailing cdr record
$this->add_cdr($this->opt);
$this->clear();
}
###################
# PRIVATE METHODS #
###################
#
# Create and send zip header for this file.
#
private function add_file_header($name, $opt, $meth, $crc, $zlen, $len) {
# strip leading slashes from file name
# (fixes bug in windows archive viewer)
$name = preg_replace('/^\\/+/', '', $name);
# calculate name length
$nlen = strlen($name);
# create dos timestamp
$opt['time'] = $opt['time'] ? $opt['time'] : time();
$dts = $this->dostime($opt['time']);
# build file header
$fields = array( # (from V.A of APPNOTE.TXT)
array('V', 0x04034b50), # local file header signature
array('v', (6 << 8) + 3), # version needed to extract
array('v', 0x00), # general purpose bit flag
array('v', $meth), # compresion method (deflate or store)
array('V', $dts), # dos timestamp
array('V', $crc), # crc32 of data
array('V', $zlen), # compressed data length
array('V', $len), # uncompressed data length
array('v', $nlen), # filename length
array('v', 0), # extra data len
);
# pack fields and calculate "total" length
$ret = $this->pack_fields($fields);
$cdr_len = strlen($ret) + $nlen + $zlen;
# print header and filename
$this->send($ret . $name);
# add to central directory record and increment offset
$this->add_to_cdr($name, $opt, $meth, $crc, $zlen, $len, $cdr_len);
}
#
# Add a large file from the given path.
#
private function add_large_file($name, $path, $opt = array()) {
$st = stat($path);
$block_size = 1048576; # process in 1 megabyte chunks
$algo = 'crc32b';
# calculate header attributes
$zlen = $len = $st['size'];
$meth_str = $this->opt['large_file_method'];
if ($meth_str == 'store') {
# store method
$meth = 0x00;
$crc = unpack('V', hash_file($algo, $path, true));
$crc = $crc[1];
} elseif ($meth_str == 'deflate') {
# deflate method
$meth = 0x08;
# open file, calculate crc and compressed file length
$fh = fopen($path, 'rb');
$hash_ctx = hash_init($algo);
$zlen = 0;
# read each block, update crc and zlen
while ($data = fgets($fh, $block_size)) {
hash_update($hash_ctx, $data);
$data = gzdeflate($data);
$zlen += strlen($data);
}
# close file and finalize crc
fclose($fh);
$crc = unpack('V', hash_final($hash_ctx, true));
$crc = $crc[1];
} else {
die("unknown large_file_method: $meth_str");
}
# send file header
$this->add_file_header($name, $opt, $meth, $crc, $zlen, $len);
# open input file
$fh = fopen($path, 'rb');
# send file blocks
while ($data = fgets($fh, $block_size)) {
if ($meth_str == 'deflate')
$data = gzdeflate($data);
# send data
$this->send($data);
}
# close input file
fclose($fh);
}
#
# Is this file larger than large_file_size?
#
function is_large_file($path) {
$st = stat($path);
return ($this->opt['large_file_size'] > 0) &&
($st['size'] > $this->opt['large_file_size']);
}
#
# Save file attributes for trailing CDR record.
#
private function add_to_cdr($name, $opt, $meth, $crc, $zlen, $len, $rec_len) {
$this->files[] = array($name, $opt, $meth, $crc, $zlen, $len, $this->ofs);
$this->ofs += $rec_len;
}
#
# Send CDR record for specified file.
#
private function add_cdr_file($args) {
list ($name, $opt, $meth, $crc, $zlen, $len, $ofs) = $args;
# get attributes
$comment = $opt['comment'] ? $opt['comment'] : '';
# get dos timestamp
$dts = $this->dostime($opt['time']);
$fields = array( # (from V,F of APPNOTE.TXT)
array('V', 0x02014b50), # central file header signature
array('v', (6 << 8) + 3), # version made by
array('v', (6 << 8) + 3), # version needed to extract
array('v', 0x00), # general purpose bit flag
array('v', $meth), # compresion method (deflate or store)
array('V', $dts), # dos timestamp
array('V', $crc), # crc32 of data
array('V', $zlen), # compressed data length
array('V', $len), # uncompressed data length
array('v', strlen($name)), # filename length
array('v', 0), # extra data len
array('v', strlen($comment)), # file comment length
array('v', 0), # disk number start
array('v', 0), # internal file attributes
array('V', 32), # external file attributes
array('V', $ofs), # relative offset of local header
);
# pack fields, then append name and comment
$ret = $this->pack_fields($fields) . $name . $comment;
$this->send($ret);
# increment cdr offset
$this->cdr_ofs += strlen($ret);
}
#
# Send CDR EOF (Central Directory Record End-of-File) record.
#
private function add_cdr_eof($opt = null) {
$num = count($this->files);
$cdr_len = $this->cdr_ofs;
$cdr_ofs = $this->ofs;
# grab comment (if specified)
$comment = '';
if ($opt && $opt['comment'])
$comment = $opt['comment'];
$fields = array( # (from V,F of APPNOTE.TXT)
array('V', 0x06054b50), # end of central file header signature
array('v', 0x00), # this disk number
array('v', 0x00), # number of disk with cdr
array('v', $num), # number of entries in the cdr on this disk
array('v', $num), # number of entries in the cdr
array('V', $cdr_len), # cdr size
array('V', $cdr_ofs), # cdr ofs
array('v', strlen($comment)), # zip file comment length
);
$ret = $this->pack_fields($fields) . $comment;
$this->send($ret);
}
#
# Add CDR (Central Directory Record) footer.
#
private function add_cdr($opt = null) {
foreach ($this->files as $file)
$this->add_cdr_file($file);
$this->add_cdr_eof($opt);
}
#
# Clear all internal variables. Note that the stream object is not
# usable after this.
#
function clear() {
$this->files = array();
$this->ofs = 0;
$this->cdr_ofs = 0;
$this->opt = array();
}
###########################
# PRIVATE UTILITY METHODS #
###########################
#
# Send HTTP headers for this stream.
#
private function send_http_headers() {
# grab options
$opt = $this->opt;
# grab content type from options
$content_type = 'application/x-zip';
if ($opt['content_type'])
$content_type = $this->opt['content_type'];
# grab content disposition
$disposition = 'attachment';
if ($opt['content_disposition'])
$disposition = $opt['content_disposition'];
if ($this->output_name)
$disposition .= "; filename=\"{$this->output_name}\"";
$headers = array(
'Content-Type' => $content_type,
'Content-Disposition' => $disposition,
'Pragma' => 'public',
'Cache-Control' => 'public, must-revalidate',
'Content-Transfer-Encoding' => 'binary',
);
foreach ($headers as $key => $val)
header("$key: $val");
}
#
# Send string, sending HTTP headers if necessary.
#
private function send($str) {
if ($this->need_headers)
$this->send_http_headers();
$this->need_headers = false;
echo $str;
}
#
# Convert a UNIX timestamp to a DOS timestamp.
#
function dostime($when = 0) {
# get date array for timestamp
$d = getdate($when);
# set lower-bound on dates
if ($d['year'] < 1980) {
$d = array('year' => 1980, 'mon' => 1, 'mday' => 1,
'hours' => 0, 'minutes' => 0, 'seconds' => 0);
}
# remove extra years from 1980
$d['year'] -= 1980;
# return date string
return ($d['year'] << 25) | ($d['mon'] << 21) | ($d['mday'] << 16) |
($d['hours'] << 11) | ($d['minutes'] << 5) | ($d['seconds'] >> 1);
}
#
# Create a format string and argument list for pack(), then call
# pack() and return the result.
#
function pack_fields($fields) {
list ($fmt, $args) = array('', array());
# populate format string and argument list
foreach ($fields as $field) {
$fmt .= $field[0];
$args[] = $field[1];
}
# prepend format string to argument list
array_unshift($args, $fmt);
# build output string from header and compressed data
return call_user_func_array('pack', $args);
}
};
?>