<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://wiki.mediagoblin.org/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Shackra</id>
	<title>GNU MediaGoblin Wiki - User contributions [en]</title>
	<link rel="self" type="application/atom+xml" href="https://wiki.mediagoblin.org/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Shackra"/>
	<link rel="alternate" type="text/html" href="https://wiki.mediagoblin.org/Special:Contributions/Shackra"/>
	<updated>2026-05-14T18:03:16Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.39.17</generator>
	<entry>
		<id>https://wiki.mediagoblin.org/index.php?title=Storage&amp;diff=1546</id>
		<title>Storage</title>
		<link rel="alternate" type="text/html" href="https://wiki.mediagoblin.org/index.php?title=Storage&amp;diff=1546"/>
		<updated>2014-07-15T02:20:05Z</updated>

		<summary type="html">&lt;p&gt;Shackra: /* The guts of StorageInterface and friends */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Being a media publishing platform, storage is a big deal in MediaGoblin.  As such there are a few systems that are storage-related that you may encounter while doing some MediaGoblin hacking.&lt;br /&gt;
&lt;br /&gt;
MediaGoblin also comes with an extensible storage interface and several implementations mapping to it: basic local file storage, OpenStack &amp;quot;swift&amp;quot; style storage... and a few more plus the ability to write your own.&lt;br /&gt;
&lt;br /&gt;
= The storage systems attached to your app =&lt;br /&gt;
&lt;br /&gt;
== Dynamic content: queue_store and public_store ==&lt;br /&gt;
&lt;br /&gt;
Two instances of the StorageInterface come attached to your app.  These are:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;queue_store:&#039;&#039;&#039; When a user submits a fresh piece of media for their gallery, before the [[Processing]] stage, that piece of media sits here in the queue_store.  (It&#039;s possible that we&#039;ll rename this to &amp;quot;private_store&amp;quot; and start storing more non-publicly-stored stuff in the future...).  This is a StorageInterface implementation instance.  Visitors to your site probably cannot see it... it isn&#039;t designed to be seen, anyway.&lt;br /&gt;
* &#039;&#039;&#039;public_store:&#039;&#039;&#039; After your media goes through processing it gets moved to the public store.  This is also a StorageInterface implelementation, and is for stuff that&#039;s intended to be seen by site visitors.&lt;br /&gt;
&lt;br /&gt;
== The workbench ==&lt;br /&gt;
&lt;br /&gt;
In addition, there&#039;s a &amp;quot;workbench&amp;quot; used during processing... it&#039;s just for temporary files during processing, and also for making local copies of stuff that might be on remote storage interfaces while transitionally moving/converting from the queue_store to the public store.  See the workbench module documentation for more.&lt;br /&gt;
&lt;br /&gt;
== Static assets / staticdirect ==&lt;br /&gt;
&lt;br /&gt;
On top of all that, there is some static media that comes bundled with your application.  This stuff is kept in:&lt;br /&gt;
&lt;br /&gt;
 mediagoblin/static/&lt;br /&gt;
&lt;br /&gt;
These files are for mediagoblin base assets.  Things like the CSS files, logos, etc.  You can mount these at whatever location is appropriate to you (see the direct_remote_path option in the config file) so if your users are keeping their static assets at http://static.mgoblin.example.org/ but their actual site is at http://mgoblin.example.org/, you need to be able to get your static files in a where-it&#039;s-mounted agnostic way.  There&#039;s a &amp;quot;staticdirector&amp;quot; attached to the request object.  It&#039;s pretty easy to use; just look at this bit taken from the mediagoblin/templates/mediagoblin/base.html main template:&lt;br /&gt;
&lt;br /&gt;
    &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; type=&amp;quot;text/css&amp;quot;&lt;br /&gt;
          href=&amp;quot;{{ request.staticdirect(&#039;/css/extlib/text.css&#039;) }}&amp;quot;/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
see?  Not too hard.  As expected, if you configured direct_remote_path to be http://static.mgoblin.example.org/ you&#039;ll get back http://static.mgoblin.example.org/css/extlib/text.css just as you&#039;d probably expect.&lt;br /&gt;
&lt;br /&gt;
= StorageInterface and implementations =&lt;br /&gt;
&lt;br /&gt;
== The guts of StorageInterface and friends ==&lt;br /&gt;
&lt;br /&gt;
So, the StorageInterface!&lt;br /&gt;
&lt;br /&gt;
So, the public and queue stores both use StorageInterface implementations... but what does that mean?  It&#039;s not too hard.&lt;br /&gt;
&lt;br /&gt;
Open up: mediagoblin/storage/__init__.py&lt;br /&gt;
&lt;br /&gt;
In here you&#039;ll see a couple of things.  First of all, there&#039;s the StorageInterface class.  What you&#039;ll see is that this is just a very simple python class.  A few of the methods actually implement things, but for the most part, they don&#039;t.  What really matters about this class is the &#039;&#039;docstrings&#039;&#039;.  Each expected method is documented as to how it should be constructed.  Want to make a new StorageInterface?  Simply subclass it.  Want to know how to use the methods of your storage system?  Read these docs, they span all implementations.&lt;br /&gt;
&lt;br /&gt;
There are a couple of implementations of these classes bundled in storage.py as well.  The most simple of these is BasicFileStorage, which is also the default storage system used.  As expected, this stores files locally on your machine.&lt;br /&gt;
&lt;br /&gt;
There&#039;s also a CloudFileStorage system.  This provides a mapping to [http://swift.openstack.org/ OpenStack&#039;s swift] storage system (used by RackSpace Cloud files and etc).&lt;br /&gt;
&lt;br /&gt;
Between these two examples you should be able to get a pretty good idea of how to write your own storage systems, for storing data across your beowulf cluster of radioactive monkey brains, whatever.&lt;br /&gt;
&lt;br /&gt;
== Writing code to store stuff ==&lt;br /&gt;
&lt;br /&gt;
So what does coding for StorageInterface implementations actually look like?  It&#039;s pretty simple, really.  For one thing, the design is fairly inspired by [https://docs.djangoproject.com/en/dev/ref/files/storage/ Django&#039;s file storage API]... with some differences.&lt;br /&gt;
&lt;br /&gt;
Basically, you access files on &amp;quot;file paths&amp;quot;, which aren&#039;t exactly like unix file paths, but are close.  If you wanted to store a file on a path like dir1/dir2/filename.jpg you&#039;d actually write that file path like:&lt;br /&gt;
&lt;br /&gt;
 [&#039;dir1&#039;, &#039;dir2&#039;, &#039;filename.jpg&#039;]&lt;br /&gt;
&lt;br /&gt;
This way we can be *sure* that each component is actually a component of the path that&#039;s expected... we do some filename cleaning on each component.&lt;br /&gt;
&lt;br /&gt;
Your StorageInterface should pass in and out &amp;quot;file like objects&amp;quot;.  In other words, they should provide .read() and .write() at minimum, and probably also .seek() and .close().&lt;/div&gt;</summary>
		<author><name>Shackra</name></author>
	</entry>
	<entry>
		<id>https://wiki.mediagoblin.org/index.php?title=Storage&amp;diff=1545</id>
		<title>Storage</title>
		<link rel="alternate" type="text/html" href="https://wiki.mediagoblin.org/index.php?title=Storage&amp;diff=1545"/>
		<updated>2014-07-15T02:14:24Z</updated>

		<summary type="html">&lt;p&gt;Shackra: /* The guts of StorageInterface and friends */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Being a media publishing platform, storage is a big deal in MediaGoblin.  As such there are a few systems that are storage-related that you may encounter while doing some MediaGoblin hacking.&lt;br /&gt;
&lt;br /&gt;
MediaGoblin also comes with an extensible storage interface and several implementations mapping to it: basic local file storage, OpenStack &amp;quot;swift&amp;quot; style storage... and a few more plus the ability to write your own.&lt;br /&gt;
&lt;br /&gt;
= The storage systems attached to your app =&lt;br /&gt;
&lt;br /&gt;
== Dynamic content: queue_store and public_store ==&lt;br /&gt;
&lt;br /&gt;
Two instances of the StorageInterface come attached to your app.  These are:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;queue_store:&#039;&#039;&#039; When a user submits a fresh piece of media for their gallery, before the [[Processing]] stage, that piece of media sits here in the queue_store.  (It&#039;s possible that we&#039;ll rename this to &amp;quot;private_store&amp;quot; and start storing more non-publicly-stored stuff in the future...).  This is a StorageInterface implementation instance.  Visitors to your site probably cannot see it... it isn&#039;t designed to be seen, anyway.&lt;br /&gt;
* &#039;&#039;&#039;public_store:&#039;&#039;&#039; After your media goes through processing it gets moved to the public store.  This is also a StorageInterface implelementation, and is for stuff that&#039;s intended to be seen by site visitors.&lt;br /&gt;
&lt;br /&gt;
== The workbench ==&lt;br /&gt;
&lt;br /&gt;
In addition, there&#039;s a &amp;quot;workbench&amp;quot; used during processing... it&#039;s just for temporary files during processing, and also for making local copies of stuff that might be on remote storage interfaces while transitionally moving/converting from the queue_store to the public store.  See the workbench module documentation for more.&lt;br /&gt;
&lt;br /&gt;
== Static assets / staticdirect ==&lt;br /&gt;
&lt;br /&gt;
On top of all that, there is some static media that comes bundled with your application.  This stuff is kept in:&lt;br /&gt;
&lt;br /&gt;
 mediagoblin/static/&lt;br /&gt;
&lt;br /&gt;
These files are for mediagoblin base assets.  Things like the CSS files, logos, etc.  You can mount these at whatever location is appropriate to you (see the direct_remote_path option in the config file) so if your users are keeping their static assets at http://static.mgoblin.example.org/ but their actual site is at http://mgoblin.example.org/, you need to be able to get your static files in a where-it&#039;s-mounted agnostic way.  There&#039;s a &amp;quot;staticdirector&amp;quot; attached to the request object.  It&#039;s pretty easy to use; just look at this bit taken from the mediagoblin/templates/mediagoblin/base.html main template:&lt;br /&gt;
&lt;br /&gt;
    &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; type=&amp;quot;text/css&amp;quot;&lt;br /&gt;
          href=&amp;quot;{{ request.staticdirect(&#039;/css/extlib/text.css&#039;) }}&amp;quot;/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
see?  Not too hard.  As expected, if you configured direct_remote_path to be http://static.mgoblin.example.org/ you&#039;ll get back http://static.mgoblin.example.org/css/extlib/text.css just as you&#039;d probably expect.&lt;br /&gt;
&lt;br /&gt;
= StorageInterface and implementations =&lt;br /&gt;
&lt;br /&gt;
== The guts of StorageInterface and friends ==&lt;br /&gt;
&lt;br /&gt;
So, the StorageInterface!&lt;br /&gt;
&lt;br /&gt;
So, the public and queue stores both use StorageInterface implementations... but what does that mean?  It&#039;s not too hard.&lt;br /&gt;
&lt;br /&gt;
Open up: mediagoblin/storage/__init__.py&lt;br /&gt;
&lt;br /&gt;
In here you&#039;ll see a couple of things.  First of all, there&#039;s the StorageInterface class.  What you&#039;ll see is that this is just a very simple python class.  A few of the methods actually implement things, but for the most part, they don&#039;t.  What really matters about this class is the &#039;&#039;docstrings&#039;&#039;.  Each expected method is documented as to how it should be constructed.  Want to make a new StorageInterface?  Simply subclass it.  Want to know how to use the methods of your storage system?  Read these docs, they span all implementations.&lt;br /&gt;
&lt;br /&gt;
There are a couple of implementations of these classes bundled in storage.py as well.  The most simple of these is BasicFileStorage, which is also the default storage system used.  As expected, this stores files locally on your machine.&lt;br /&gt;
&lt;br /&gt;
There&#039;s also a CloudFileStorage system.  This provides a mapping to [OpenStack&#039;s swift http://swift.openstack.org/] storage system (used by RackSpace Cloud files and etc).&lt;br /&gt;
&lt;br /&gt;
Between these two examples you should be able to get a pretty good idea of how to write your own storage systems, for storing data across your beowulf cluster of radioactive monkey brains, whatever.&lt;br /&gt;
&lt;br /&gt;
== Writing code to store stuff ==&lt;br /&gt;
&lt;br /&gt;
So what does coding for StorageInterface implementations actually look like?  It&#039;s pretty simple, really.  For one thing, the design is fairly inspired by [https://docs.djangoproject.com/en/dev/ref/files/storage/ Django&#039;s file storage API]... with some differences.&lt;br /&gt;
&lt;br /&gt;
Basically, you access files on &amp;quot;file paths&amp;quot;, which aren&#039;t exactly like unix file paths, but are close.  If you wanted to store a file on a path like dir1/dir2/filename.jpg you&#039;d actually write that file path like:&lt;br /&gt;
&lt;br /&gt;
 [&#039;dir1&#039;, &#039;dir2&#039;, &#039;filename.jpg&#039;]&lt;br /&gt;
&lt;br /&gt;
This way we can be *sure* that each component is actually a component of the path that&#039;s expected... we do some filename cleaning on each component.&lt;br /&gt;
&lt;br /&gt;
Your StorageInterface should pass in and out &amp;quot;file like objects&amp;quot;.  In other words, they should provide .read() and .write() at minimum, and probably also .seek() and .close().&lt;/div&gt;</summary>
		<author><name>Shackra</name></author>
	</entry>
	<entry>
		<id>https://wiki.mediagoblin.org/index.php?title=Git_workflow&amp;diff=1544</id>
		<title>Git workflow</title>
		<link rel="alternate" type="text/html" href="https://wiki.mediagoblin.org/index.php?title=Git_workflow&amp;diff=1544"/>
		<updated>2014-07-14T06:15:57Z</updated>

		<summary type="html">&lt;p&gt;Shackra: /* Contributing changes */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;GNU MediaGoblin uses git for all our version control and we have the&lt;br /&gt;
repositories hosted on [http://gitorious.org/ Gitorious].  We have&lt;br /&gt;
two repositories:&lt;br /&gt;
&lt;br /&gt;
* MediaGoblin software: http://gitorious.org/mediagoblin/mediagoblin&lt;br /&gt;
* MediaGoblin website: http://gitorious.org/mediagoblin/mediagoblin-website&lt;br /&gt;
&lt;br /&gt;
It&#039;s most likely you want to look at the software repository -- not the&lt;br /&gt;
website one.&lt;br /&gt;
&lt;br /&gt;
The rest of this chapter talks about using the software repository.&lt;br /&gt;
&lt;br /&gt;
The short of it is: we do not use merge requests.  Instead, create a &amp;quot;feature branch&amp;quot; in git that you push somewhere, and link to it on a ticket.  Details below.&lt;br /&gt;
&lt;br /&gt;
= How to clone the project =&lt;br /&gt;
&lt;br /&gt;
Do::&lt;br /&gt;
&lt;br /&gt;
    git clone git://gitorious.org/mediagoblin/mediagoblin.git&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
= How to contribute changes =&lt;br /&gt;
&lt;br /&gt;
== Tie your changes to issues in the issue tracker ==&lt;br /&gt;
&lt;br /&gt;
All patches should be tied to issues in the&lt;br /&gt;
[http://issues.mediagoblin.org/ issue tracker].&lt;br /&gt;
That makes it a lot easier for everyone to track proposed changes and&lt;br /&gt;
make sure your hard work doesn&#039;t get dropped on the floor!  If there&lt;br /&gt;
isn&#039;t an issue for what you&#039;re working on, please create one.  The&lt;br /&gt;
better the description of what it is you&#039;re trying to fix/implement,&lt;br /&gt;
the better everyone else is able to understand why you&#039;re doing what&lt;br /&gt;
you&#039;re doing.&lt;br /&gt;
&lt;br /&gt;
== Use bugfix branches to make changes ==&lt;br /&gt;
&lt;br /&gt;
The best way to isolate your changes is to create a branch based off&lt;br /&gt;
of the MediaGoblin repository master branch, do the changes related to&lt;br /&gt;
that one issue there, and then let us know how to get it.&lt;br /&gt;
&lt;br /&gt;
It&#039;s much easier on us if you isolate your changes to a branch focused&lt;br /&gt;
on the issue.  Then we don&#039;t have to sift through things.&lt;br /&gt;
&lt;br /&gt;
It&#039;s much easier on you if you isolate your changes to a branch&lt;br /&gt;
focused on the issue.  Then when we merge your changes in, you just&lt;br /&gt;
have to do a {{Cmd|git fetch}} and that&#039;s it.  This is especially true if&lt;br /&gt;
we reject some of your changes, but accept others or otherwise tweak&lt;br /&gt;
your changes.&lt;br /&gt;
&lt;br /&gt;
Further, if you isolate your changes to a branch, then you can work on&lt;br /&gt;
multiple issues at the same time and they don&#039;t conflict with one&lt;br /&gt;
another.&lt;br /&gt;
&lt;br /&gt;
Name your branches using the isue number and something that makes it clear&lt;br /&gt;
what it&#039;s about.  For example, if you were working on tagging, you&lt;br /&gt;
might name your branch &amp;quot;360_tagging&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
== Properly document your changes ==&lt;br /&gt;
&lt;br /&gt;
Include comments in the code.&lt;br /&gt;
&lt;br /&gt;
Write comprehensive commit messages.  The better your commit message&lt;br /&gt;
is at describing what you did and why, the easier it is for us to&lt;br /&gt;
quickly accept your patch.&lt;br /&gt;
&lt;br /&gt;
Write comprehensive comments in the issue tracker about what you&#039;re&lt;br /&gt;
doing and why.&lt;br /&gt;
&lt;br /&gt;
== How to send us your changes ==&lt;br /&gt;
&lt;br /&gt;
There are two ways to let us know how to get it:&lt;br /&gt;
&lt;br /&gt;
=== push changes to publicly available git clone and let us know where to find it ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;This is the preferred method of sending changes.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Push your feature/bugfix/issue branch to your publicly available&lt;br /&gt;
git clone and add a comment to the issue with the url for your&lt;br /&gt;
clone and the branch to look at.&lt;br /&gt;
&lt;br /&gt;
=== attaching the patch files to the issue ===&lt;br /&gt;
&lt;br /&gt;
Run&lt;br /&gt;
&lt;br /&gt;
{{Cmd|git format-patch --stdout &amp;lt;remote&amp;gt;/master &amp;gt; issue_&amp;lt;number&amp;gt;.patch}}&lt;br /&gt;
       &lt;br /&gt;
&amp;lt;tt&amp;gt;format-patch&amp;lt;/tt&amp;gt; creates a patch of all the commits that are in&lt;br /&gt;
your branch that aren&#039;t in &amp;lt;tt&amp;gt;&amp;lt;remote&amp;gt;/master&amp;lt;/tt&amp;gt;.  The &amp;lt;tt&amp;gt;--stdout&amp;lt;/tt&amp;gt;&lt;br /&gt;
flag causes all this output to go to stdout where it&#039;s redirected&lt;br /&gt;
to a file named &amp;lt;tt&amp;gt;issue_&amp;lt;number&amp;gt;.patch&amp;lt;/tt&amp;gt;.  That file should be&lt;br /&gt;
based on the issue you&#039;re working with.  For example,&lt;br /&gt;
&amp;lt;tt&amp;gt;issue_42.patch&amp;lt;/tt&amp;gt; is a good filename and &amp;lt;tt&amp;gt;issue_42_rev2.patch&amp;lt;/tt&amp;gt;&lt;br /&gt;
is good if you did a revision of it.&lt;br /&gt;
&lt;br /&gt;
Having said all that, the filename isn&#039;t wildly important.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
= Example workflow =&lt;br /&gt;
&lt;br /&gt;
Here&#039;s an example workflow.&lt;br /&gt;
&lt;br /&gt;
== Contributing changes ==&lt;br /&gt;
&lt;br /&gt;
Slartibartfast from the planet Magrathea far off in the universe has&lt;br /&gt;
decided that he is bored with fjords and wants to fix issue 42 (the&lt;br /&gt;
meaning of life bug) and send us the changes.&lt;br /&gt;
&lt;br /&gt;
Slartibartfast has cloned the MediaGoblin repository and his clone&lt;br /&gt;
lives on gitorious.&lt;br /&gt;
&lt;br /&gt;
Slartibartfast works locally.  The remote named ``origin`` points to&lt;br /&gt;
his clone on gitorious.  The remote named ``gmg`` points to the&lt;br /&gt;
MediaGoblin repository.&lt;br /&gt;
&lt;br /&gt;
Slartibartfast does the following:&lt;br /&gt;
&lt;br /&gt;
1. Fetches the latest from the MediaGoblin repository::&lt;br /&gt;
&lt;br /&gt;
 git fetch --all -p&lt;br /&gt;
&lt;br /&gt;
This tells &amp;lt;tt&amp;gt;git fetch&amp;lt;/tt&amp;gt; to fetch all the recent data from all of&lt;br /&gt;
the remotes (&amp;lt;tt&amp;gt;--all&amp;lt;/tt&amp;gt;) and prune any branches that have been&lt;br /&gt;
deleted in the remotes (&amp;lt;tt&amp;gt;-p&amp;lt;/tt&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
2. Creates a branch from the tip of the MediaGoblin repository (the remote is named &amp;lt;tt&amp;gt;gmg&amp;lt;/tt&amp;gt;) master branch called &amp;lt;tt&amp;gt;bug42_meaning_of_life&amp;lt;/tt&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
 git checkout -b bug42_meaning_of_life gmg/master&lt;br /&gt;
&lt;br /&gt;
This creates a new branch (&amp;lt;tt&amp;gt;-b&amp;lt;/tt&amp;gt;) named &amp;lt;tt&amp;gt;bug42_meaning_of_life&amp;lt;/tt&amp;gt; based&lt;br /&gt;
on the tip of the &amp;lt;tt&amp;gt;master&amp;lt;/tt&amp;gt; branch of the remote named &amp;lt;tt&amp;gt;gmg&amp;lt;/tt&amp;gt; and checks&lt;br /&gt;
it out.&lt;br /&gt;
&lt;br /&gt;
3. Slartibartfast works hard on his changes in the &amp;lt;tt&amp;gt;bug42_meaning_of_life&amp;lt;/tt&amp;gt; branch.  When done, he wants to notify us that he has made changes he wants us to see.&lt;br /&gt;
&lt;br /&gt;
4. Slartibartfast pushes his changes to his clone:&lt;br /&gt;
&lt;br /&gt;
 git push origin bug42_meaning_of_life --set-upstream&lt;br /&gt;
&lt;br /&gt;
This pushes the changes in the &amp;lt;tt&amp;gt;bug42_meaning_of_life&amp;lt;/tt&amp;gt; branch to the remote named &amp;lt;tt&amp;gt;origin&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
5. Slartibartfast adds a comment to issue 42 with the url for his repository and the name of the branch he put the code in.  He also explains what he did and why it addresses the issue.&lt;br /&gt;
&lt;br /&gt;
== Updating a contribution ==&lt;br /&gt;
&lt;br /&gt;
Slartibartfast brushes his hands off with the sense of accomplishment&lt;br /&gt;
that comes with the knowledge of a job well done.  He stands, wanders&lt;br /&gt;
over to get a cup of water, then realizes that he forgot to run the&lt;br /&gt;
unit tests!&lt;br /&gt;
&lt;br /&gt;
He runs the unit tests and discovers there&#039;s a bug in the code!&lt;br /&gt;
&lt;br /&gt;
Then he does this:&lt;br /&gt;
&lt;br /&gt;
1. He checks out the &amp;lt;tt&amp;gt;bug42_meaning_of_life&amp;lt;/tt&amp;gt; branch::&lt;br /&gt;
&lt;br /&gt;
 git checkout bug42_meaning_of_life&lt;br /&gt;
&lt;br /&gt;
2. He fixes the bug and checks it into the &amp;lt;tt&amp;gt;bug42_meaning_of_life&amp;lt;/tt&amp;gt; branch.&lt;br /&gt;
&lt;br /&gt;
3. He pushes his changes to his clone (the remote is named &amp;lt;tt&amp;gt;origin&amp;lt;/tt&amp;gt;):&lt;br /&gt;
&lt;br /&gt;
 git push origin bug42_meaning_of_life&lt;br /&gt;
&lt;br /&gt;
4. He adds another comment to issue 42 explaining about the mistake and how he fixed it and that he&#039;s pushed the new change to the &amp;lt;tt&amp;gt;bug42_meaning_of_life&amp;lt;/tt&amp;gt; branch of his publicly available clone.&lt;br /&gt;
&lt;br /&gt;
== What happens next ==&lt;br /&gt;
&lt;br /&gt;
Slartibartfast is once again happy with his work.  He finds issue 42&lt;br /&gt;
in the issue tracker and adds a comment saying he submitted a merge&lt;br /&gt;
request with his changes and explains what they are.&lt;br /&gt;
: &amp;quot;merge request&amp;quot;? https://gitorious.org/mediagoblin says &amp;quot;We don&#039;t use no stinking merge requests&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Later, someone checks out his code and finds a problem with it.  He&lt;br /&gt;
adds a comment to the issue tracker specifying the problem and asks&lt;br /&gt;
Slartibartfast to fix it.  Slartibartfst goes through the above steps&lt;br /&gt;
again, fixes the issue, pushes it to his&lt;br /&gt;
&amp;lt;tt&amp;gt;bug42_meaning_of_life&amp;lt;/tt&amp;gt; branch and adds another comment to the&lt;br /&gt;
issue tracker about how he fixed it.&lt;br /&gt;
&lt;br /&gt;
Later, someone checks out his code and is happy with it.  Someone&lt;br /&gt;
pulls it into the master branch of the MediaGoblin repository and adds&lt;br /&gt;
another comment to the issue and probably closes the issue out.&lt;br /&gt;
&lt;br /&gt;
Slartibartfast is notified of this.  Slartibartfast does a:&lt;br /&gt;
&lt;br /&gt;
 git fetch --all&lt;br /&gt;
&lt;br /&gt;
The changes show up in the &amp;lt;tt&amp;gt;master&amp;lt;/tt&amp;gt; branch of the &amp;lt;tt&amp;gt;gmg&amp;lt;/tt&amp;gt; remote.&lt;br /&gt;
Slartibartfast now deletes his &amp;lt;tt&amp;gt;bug42_meaning_of_life&amp;lt;/tt&amp;gt; branch&lt;br /&gt;
because he doesn&#039;t need it anymore.&lt;br /&gt;
&lt;br /&gt;
= How to learn git =&lt;br /&gt;
&lt;br /&gt;
[[BeginnersCorner#Learning_git]]&lt;/div&gt;</summary>
		<author><name>Shackra</name></author>
	</entry>
	<entry>
		<id>https://wiki.mediagoblin.org/index.php?title=HackingHowto&amp;diff=1543</id>
		<title>HackingHowto</title>
		<link rel="alternate" type="text/html" href="https://wiki.mediagoblin.org/index.php?title=HackingHowto&amp;diff=1543"/>
		<updated>2014-07-14T05:14:42Z</updated>

		<summary type="html">&lt;p&gt;Shackra: /* ArchLinux / Parabola */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Hacking HOWTO =&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== So you want to hack on GNU MediaGoblin? ==&lt;br /&gt;
&lt;br /&gt;
First thing to do is check out the [http://mediagoblin.org/join/ web site] where we list all the project&lt;br /&gt;
infrastructure including:&lt;br /&gt;
&lt;br /&gt;
* the IRC channel&lt;br /&gt;
* the mailing list&lt;br /&gt;
* the issue tracker&lt;br /&gt;
&lt;br /&gt;
Additionally, we have information on how to get involved, who to talk&lt;br /&gt;
to, what needs to be worked on, and other things besides!&lt;br /&gt;
&lt;br /&gt;
Second thing to do is take a look at [http://docs.mediagoblin.org/devel/codebase.html codebase chapter] where&lt;br /&gt;
we&#039;ve started documenting how GNU MediaGoblin is built and how to add&lt;br /&gt;
new things.  If you&#039;re planning on contributing in python, you should be aware&lt;br /&gt;
of [http://www.python.org/dev/peps/pep-0008/ PEP-8], the official Python style guide,&lt;br /&gt;
which we follow.&lt;br /&gt;
&lt;br /&gt;
Third you&#039;ll need to get the requirements.&lt;br /&gt;
&lt;br /&gt;
Fourth, you&#039;ll need to build a development environment.  We use an&lt;br /&gt;
in-package checkout of virtualenv.  This isn&#039;t the convenional way to&lt;br /&gt;
install virtualenv (normally you don&#039;t install virtualenv inside the&lt;br /&gt;
package itself) but we&#039;ve found that it&#039;s significantly easier for&lt;br /&gt;
newcomers who aren&#039;t already familiar with virtualenv.  If you *are*&lt;br /&gt;
already familiar with virtualenv, feel free to just install&lt;br /&gt;
mediagoblin in your own virtualenv setup... the necessary adjustments&lt;br /&gt;
should be obvious.&lt;br /&gt;
&lt;br /&gt;
== Getting requirements ==&lt;br /&gt;
&lt;br /&gt;
First, you need to have the following installed before you can build&lt;br /&gt;
an environment for hacking on GNU MediaGoblin:&lt;br /&gt;
&lt;br /&gt;
* Python 2.6 or 2.7  - http://www.python.org/ (You&#039;ll need Python as well as the dev files for building modules.)&lt;br /&gt;
* python-lxml        - http://lxml.de/&lt;br /&gt;
* git                - http://git-scm.com/&lt;br /&gt;
* SQLAlchemy 0.7.0 or higher   - http://www.sqlalchemy.org/&lt;br /&gt;
* Python Imaging Library (PIL) - http://www.pythonware.com/products/pil/&lt;br /&gt;
* virtualenv         - http://www.virtualenv.org/&lt;br /&gt;
* Python GStreamer Bindings - http://gstreamer.freedesktop.org/modules/gst-python.html&lt;br /&gt;
&lt;br /&gt;
=== GNU/Linux ===&lt;br /&gt;
&lt;br /&gt;
==== Debian and derivatives ====&lt;br /&gt;
&lt;br /&gt;
If you&#039;re running Debian GNU/Linux or a Debian-derived distribution&lt;br /&gt;
such as Debian, Mint, or [http://bugs.foocorp.net/issues/478 Ubuntu 10.10+], running the following should install these&lt;br /&gt;
requirements:&lt;br /&gt;
&lt;br /&gt;
{{Cmd|sudo apt-get install git-core python python-dev python-lxml python-imaging python-virtualenv python-gst0.10 libjpeg8-dev}}&lt;br /&gt;
&lt;br /&gt;
==== Fedora / RedHat(?) ====&lt;br /&gt;
&lt;br /&gt;
On Fedora:&lt;br /&gt;
&lt;br /&gt;
{{Cmd|yum install python-paste-deploy python-paste-script git-core python python-devel python-lxml python-imaging python-virtualenv gstreamer-python}}&lt;br /&gt;
&lt;br /&gt;
==== ArchLinux / Parabola ====&lt;br /&gt;
&lt;br /&gt;
The following command should work (&amp;lt;del&amp;gt;not tested on a new ArchLinux / Parabola install&amp;lt;/del&amp;gt;. tested, it works):&lt;br /&gt;
&lt;br /&gt;
{{Cmd|pacman -S git python2 python2-lxml python2-pillow python2-virtualenv gstreamer0.10-python}}&lt;br /&gt;
&lt;br /&gt;
=== Mac OS X ===&lt;br /&gt;
&lt;br /&gt;
==== Mac OS X Lion ====&lt;br /&gt;
&lt;br /&gt;
Download the Newest Python.&lt;br /&gt;
&lt;br /&gt;
Git is already installed.&lt;br /&gt;
&lt;br /&gt;
* Note for PIL and lxml, you can: pip install pil lxml&lt;br /&gt;
&lt;br /&gt;
Python-lxml: http://muffinresearch.co.uk/archives/2009/03/05/install-lxml-on-osx/ with sudo&lt;br /&gt;
&lt;br /&gt;
Python Imaging Library (PIL): http://code.google.com/appengine/docs/python/images/installingPIL.html#mac&lt;br /&gt;
&lt;br /&gt;
Libjpeg &amp;amp; Libpng: http://ethan.tira-thompson.com/Mac_OS_X_Ports.html Combo Installer&lt;br /&gt;
&lt;br /&gt;
==== Mac OS X Snow Leopard ====&lt;br /&gt;
&lt;br /&gt;
# You will probably want to install MacPorts this will give you access to many free software packages in the same manner to apt-get and yum: https://www.macports.org/install.php&lt;br /&gt;
# Ensure you install Git and the command line tools: https://help.github.com/articles/set-up-git#platform-mac&lt;br /&gt;
# Once both of those are installed type this in your terminal and enter your password when prompted for it {{Cmd|sudo port install python27 py27-lxml py27-sqlalchemy py27-pil py27-virtualenv py27-gst-python py27-pastescript}}&lt;br /&gt;
&lt;br /&gt;
=== Microsoft Windows ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Thanks wctype!&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
==== Getting requirements ====&lt;br /&gt;
&lt;br /&gt;
* Python 2.7  -  [http://www.python.org/download/ Download] &amp;lt;!-- http://www.python.org/ftp/python/2.7.3/python-2.7.3.msi --&amp;gt;&lt;br /&gt;
* git - [https://github.com/msysgit/git/downloads Download] &amp;lt;!-- https://github.com/downloads/msysgit/git/Git-1.7.11-preview20120620.exe --&amp;gt;&lt;br /&gt;
* python-lxml - [http://pypi.python.org/pypi/lxml/2.3.5#downloads Tarball] [http://www.lfd.uci.edu/~gohlke/pythonlibs/#pil Binaries] &amp;lt;!-- http://pypi.python.org/packages/source/l/lxml/lxml-2.3.5.tar.gz, http://www.lfd.uci.edu/~gohlke/pythonlibs/z8sp4uqu/lxml-2.3.5.win32-py2.7.exe --&amp;gt;&lt;br /&gt;
* Python Imaging Library (PIL) - [http://www.pythonware.com/products/pil/ Download] &amp;lt;!-- http://effbot.org/downloads/PIL-1.1.7.win32-py2.7.exe] --&amp;gt;&lt;br /&gt;
* virtualenv - [http://pypi.python.org/pypi/virtualenvwrapper-win/1.0.8#downloads Download] &amp;lt;!-- http://pypi.python.org/packages/source/v/virtualenvwrapper-win/virtualenvwrapper-win-1.0.8.zip --&amp;gt;&lt;br /&gt;
* OSSBuild project provides reasonably up-to-date binaries of GStreamer - [https://code.google.com/p/ossbuild/downloads/list Download] &amp;lt;!-- http://ossbuild.googlecode.com/files/GStreamer-WinBuilds-GPL-x86.msi --&amp;gt;&lt;br /&gt;
* py-bcrypt - [https://bitbucket.org/alexandrul/py-bcrypt/downloads/ Download] &amp;lt;!-- https://bitbucket.org/alexandrul/py-bcrypt/downloads/py-bcrypt-0.2.post1.win32-py2.7.exe --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;You can help:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
If you have instructions for other GNU/Linux distributions, Windows, or Mac OS X to set&lt;br /&gt;
up requirements, [http://mediagoblin.org/join/ let us know]!&lt;br /&gt;
&lt;br /&gt;
== How to set up and maintain an environment for hacking with virtualenv ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Requirements&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
No additional requirements.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Create a development environment&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
After installing the requirements, follow these steps:&lt;br /&gt;
&lt;br /&gt;
* Clone the repository: {{Cmd|git clone &amp;lt;nowiki&amp;gt;git://gitorious.org/mediagoblin/mediagoblin.git&amp;lt;/nowiki&amp;gt;}}&lt;br /&gt;
* Change directories to your new checkout: {{Cmd|cd mediagoblin}}&lt;br /&gt;
* Checkout git submodules: {{Cmd|git submodule init}} {{Cmd|git submodule update}}&lt;br /&gt;
* Set up the in-package virtualenv:&lt;br /&gt;
  (virtualenv --system-site-packages . || virtualenv .) &lt;br /&gt;
* Run setup.py:&lt;br /&gt;
  {{Cmd|./bin/python setup.py develop}}&lt;br /&gt;
* Init the database:&lt;br /&gt;
  {{Cmd|./bin/gmg dbupdate}}&lt;br /&gt;
&lt;br /&gt;
That&#039;s it!&lt;br /&gt;
&lt;br /&gt;
If you want to make sure things are working, consider running the test suite:&lt;br /&gt;
  {{Cmd|./runtests.sh}}&lt;br /&gt;
&lt;br /&gt;
(If you have troubles in the remaining steps, consider try installing&lt;br /&gt;
virtualenv with one of the flags --setuptools, --distribute or possibly --no-site-packages.  Additionally, if your system has python3.X as the default, you might need to do virtualenv --python=python2.7 or --python=python2.6)&lt;br /&gt;
&lt;br /&gt;
If you have problems, please [http://mediagoblin.org/join/ let us know]!&lt;br /&gt;
&lt;br /&gt;
== Updating an existing environment ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Updating for dependency changes&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
While hacking on GNU MediaGoblin over time, you&#039;ll eventually have to&lt;br /&gt;
update your development environment because the dependencies have&lt;br /&gt;
changed.&lt;br /&gt;
&lt;br /&gt;
To do that, run:&lt;br /&gt;
&lt;br /&gt;
{{Cmd|./bin/python setup.py develop --upgrade &amp;amp;&amp;amp; ./bin/gmg dbupdate}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Updating for code changes&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{{Cmd|git pull -u}}&lt;br /&gt;
{{Cmd|git submodule update}}&lt;br /&gt;
&lt;br /&gt;
== Running the server ==&lt;br /&gt;
&lt;br /&gt;
If you want to get things running quickly and without hassle, just&lt;br /&gt;
run:&lt;br /&gt;
&lt;br /&gt;
{{Cmd|./lazyserver.sh}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This will start up a python server where you can begin playing with&lt;br /&gt;
mediagoblin, listening on 127.0.0.1:6543.  It will also run celery in &amp;quot;always eager&amp;quot; mode so you&lt;br /&gt;
don&#039;t have to start a separate process for it.&lt;br /&gt;
&lt;br /&gt;
By default, the instance is not sending out confirmation mails. Instead they are redirected to the standard output (the console) of lazyserver.sh.&lt;br /&gt;
&lt;br /&gt;
You can change this behavior setting &amp;lt;code&amp;gt;email_debug_mode&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt; in mediagoblin.ini&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This is fine in development, but if you want to actually run celery&lt;br /&gt;
separately for testing (or deployment purposes), you&#039;ll want to run&lt;br /&gt;
the server independently:&lt;br /&gt;
&lt;br /&gt;
{{Cmd|./bin/paster serve paste.ini --reload}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Running celeryd ==&lt;br /&gt;
&lt;br /&gt;
If you aren&#039;t using &amp;lt;tt&amp;gt;./lazyserver.sh&amp;lt;/tt&amp;gt; or otherwise aren&#039;t running celery&lt;br /&gt;
in always eager mode, you&#039;ll need to do this if you want your media to&lt;br /&gt;
process and actually show up.  It&#039;s probably a good idea in&lt;br /&gt;
development to have the web server (above) running in one terminal and&lt;br /&gt;
celeryd in another window.&lt;br /&gt;
&lt;br /&gt;
Run:&lt;br /&gt;
&lt;br /&gt;
{{Cmd|&amp;lt;nowiki&amp;gt;CELERY_CONFIG_MODULE=mediagoblin.init.celery.from_celery ./bin/celeryd&amp;lt;/nowiki&amp;gt;}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Running the test suite ==&lt;br /&gt;
&lt;br /&gt;
Run:&lt;br /&gt;
&lt;br /&gt;
{{Cmd|./runtests.sh}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Running a shell ==&lt;br /&gt;
&lt;br /&gt;
If you want a shell with your database pre-setup and an instantiated&lt;br /&gt;
application ready and at your fingertips....&lt;br /&gt;
&lt;br /&gt;
Run:&lt;br /&gt;
&lt;br /&gt;
{{Cmd|./bin/gmg shell}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Troubleshooting ==&lt;br /&gt;
&lt;br /&gt;
== Wiping your user data ==&lt;br /&gt;
&lt;br /&gt;
You can completely wipe all data from the instance by doing:&lt;br /&gt;
&lt;br /&gt;
{{Cmd|rm -rf mediagoblin.db kombu.db celery.db user_dev; ./bin/gmg dbupdate}}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Unless you&#039;re doing development and working on and testing creating&lt;br /&gt;
a new instance, you will probably never have to do this.&lt;br /&gt;
&lt;br /&gt;
== Quickstart for Django programmers ==&lt;br /&gt;
&lt;br /&gt;
We&#039;re not using Django, but the codebase is very Django-like in its&lt;br /&gt;
structure.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;tt&amp;gt;routing.py&amp;lt;/tt&amp;gt; is like &amp;lt;tt&amp;gt;urls.py&amp;lt;/tt&amp;gt; in Django&lt;br /&gt;
* &amp;lt;tt&amp;gt;models.py&amp;lt;/tt&amp;gt; has SQLAlchemy ORM definitions&lt;br /&gt;
* &amp;lt;tt&amp;gt;views.py&amp;lt;/tt&amp;gt; is where the views go&lt;br /&gt;
&lt;br /&gt;
We&#039;re using SQLAlchemy, which is semi-similar to the Django ORM, but&lt;br /&gt;
not really because you can get a lot more fine-grained.  The&lt;br /&gt;
[http://docs.sqlalchemy.org/en/latest/orm/tutorial.html SQLAlchemy ORM tutorial] is a great place to start.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;YouCanHelp&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
If there are other things that you think would help orient someone&lt;br /&gt;
new to GNU MediaGoblin but coming from Django, let us know!&lt;br /&gt;
&lt;br /&gt;
== Showing off your work with PageKite ==&lt;br /&gt;
&lt;br /&gt;
If you&#039;re doing development with MediaGoblin, it&#039;s sometimes helpful to show off your work to gather feedback from other contributors.  A number of the MediaGoblin developers use something called [http://pagekite.net PageKite], which is a fellow free software web service which makes temporarily showing off work on your machine easy.  There&#039;s a [http://pagekite.net/wiki/Howto/UsePageKiteWithMediaGoblin/ tutorial on how to use PageKite and MediaGoblin together] available on the PageKite wiki.&lt;br /&gt;
&lt;br /&gt;
If you are doing a lot of MediaGoblin development, the PageKite people have graciously offered us a good amount of bandwidth at no cost in an effort to help out fellow free software projects.  If you&#039;ve been making significant contributions, PM Chris Webber on freenode (who is paroneayea there) and ask if you can be added to our group plan.&lt;br /&gt;
&lt;br /&gt;
== Bite-sized bugs to start with ==&lt;br /&gt;
&lt;br /&gt;
Now you should visit our latest list of [http://issues.mediagoblin.org/query?status=!closed&amp;amp;keywords=~bitesized bite-sized issues] because squishing bugs is messy fun. If you&#039;re interested in other things to work on, or need help getting started on a bug, let us know on [http://mediagoblin.org/join/ the mailing list] or on the [http://mediagoblin.org/join/ IRC channel].&lt;/div&gt;</summary>
		<author><name>Shackra</name></author>
	</entry>
	<entry>
		<id>https://wiki.mediagoblin.org/index.php?title=Deployment&amp;diff=1294</id>
		<title>Deployment</title>
		<link rel="alternate" type="text/html" href="https://wiki.mediagoblin.org/index.php?title=Deployment&amp;diff=1294"/>
		<updated>2013-05-22T22:10:08Z</updated>

		<summary type="html">&lt;p&gt;Shackra: libapache2-mod-fcgi is on main http://packages.debian.org/wheezy/libapache2-mod-fcgid&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page could use a lot of work.  For now, a few smaller deployment tips!&lt;br /&gt;
&lt;br /&gt;
See also: http://docs.mediagoblin.org/deploying.html (some of which may belong here)&lt;br /&gt;
&lt;br /&gt;
= FCGI script =&lt;br /&gt;
&lt;br /&gt;
This works great with the apache FCGID config example in the next section :) in which case you should name it &amp;quot;mg.fcgi&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Before use, make sure you replace &#039;/path/to/mediagoblin/bin/python&#039; with a real path on your server, e.g. &#039;/srv/www/myhomepage.com/mediagoblin/bin/python&#039;. Also replace &#039;/path/to/mediagoblin/paste.ini&#039;.&lt;br /&gt;
&lt;br /&gt;
If you encounter problems, try executing executing the script manually, e.g. &amp;lt;pre&amp;gt;./mg.fcgi&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Script:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;#!/path/to/mediagoblin/bin/python&lt;br /&gt;
&lt;br /&gt;
# Written in 2011 by Christopher Allan Webber&lt;br /&gt;
#&lt;br /&gt;
# To the extent possible under law, the author(s) have dedicated all&lt;br /&gt;
# copyright and related and neighboring rights to this software to the&lt;br /&gt;
# public domain worldwide. This software is distributed without any&lt;br /&gt;
# warranty.&lt;br /&gt;
# &lt;br /&gt;
# You should have received a copy of the CC0 Public Domain Dedication along&lt;br /&gt;
# with this software. If not, see&lt;br /&gt;
# &amp;lt;http://creativecommons.org/publicdomain/zero/1.0/&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
from paste.deploy import loadapp&lt;br /&gt;
from flup.server.fcgi import WSGIServer&lt;br /&gt;
&lt;br /&gt;
CONFIG_PATH = &#039;/path/to/mediagoblin/paste.ini&#039;&lt;br /&gt;
&lt;br /&gt;
## Uncomment this to run celery in &amp;quot;always eager&amp;quot; mode... ie, you don&#039;t have&lt;br /&gt;
## to run a separate process, but submissions wait till processing finishes&lt;br /&gt;
# import os&lt;br /&gt;
# os.environ[&#039;CELERY_ALWAYS_EAGER&#039;] = &#039;true&#039;&lt;br /&gt;
&lt;br /&gt;
def launch_fcgi():&lt;br /&gt;
    ccengine_wsgi_app = loadapp(&#039;config:&#039; + CONFIG_PATH)&lt;br /&gt;
    WSGIServer(ccengine_wsgi_app).run()&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
if __name__ == &#039;__main__&#039;:&lt;br /&gt;
    launch_fcgi()&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Apache 2 Config with fcgid =&lt;br /&gt;
&lt;br /&gt;
Note that the libapache2-mod-fcgi in Debian is in the main section. libapache2-mod-fcgid can be used, but requires a slightly different configuration.&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;VirtualHost *:80&amp;gt;&lt;br /&gt;
    Options +ExecCGI&lt;br /&gt;
  &lt;br /&gt;
    # Accept up to 16MB requests&lt;br /&gt;
    FcgidMaxRequestLen 16777216&lt;br /&gt;
  &lt;br /&gt;
    ServerName mediagoblin.example.org&lt;br /&gt;
  &lt;br /&gt;
    Alias /mgoblin_static/ /path/to/mediagoblin/mediagoblin/static/&lt;br /&gt;
    Alias /mgoblin_media/ /path/to/mediagoblin/user_dev/media/public/&lt;br /&gt;
  &lt;br /&gt;
    ScriptAlias / /path/to/mediagoblin/mg.fcgi/&lt;br /&gt;
  &amp;lt;/VirtualHost&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Apache Config Example =&lt;br /&gt;
This configuration example uses mod_fastcgi.&lt;br /&gt;
&lt;br /&gt;
To install and enable mod_fastcgi on a Debian/Ubuntu based system:&lt;br /&gt;
&amp;lt;pre&amp;gt;# apt-get install libapache2-mod-suexec libapache2-mod-fastcgi&lt;br /&gt;
# a2enmod suexec&lt;br /&gt;
# a2enmod fastcgi&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Sample configuration:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;VirtualHost *:80&amp;gt;&lt;br /&gt;
ServerName mediagoblin.yourdomain.tld&lt;br /&gt;
ServerAdmin webmaster@yourdoimain.tld&lt;br /&gt;
DocumentRoot /var/www/&lt;br /&gt;
# Custom log files&lt;br /&gt;
CustomLog /var/log/apache2/mediagobling_access.log combined&lt;br /&gt;
ErrorLog /var/log/apache2/mediagoblin_error.log&lt;br /&gt;
&lt;br /&gt;
# Serve static and media files via alias&lt;br /&gt;
Alias /mgoblin_static/ /path/to/mediagoblin/mediagoblin/static/&lt;br /&gt;
Alias /mgoblin_media/ /path/to/mediagoblin/user_dev/media/public/&lt;br /&gt;
&lt;br /&gt;
# Rewrite all URLs to fcgi, except for static and media urls&lt;br /&gt;
RewriteEngine On&lt;br /&gt;
RewriteRule ^(mgoblin_static|mgoblin_media)($|/) - [L]&lt;br /&gt;
RewriteCond %{REQUEST_FILENAME} !-f&lt;br /&gt;
RewriteRule ^/(.*)$ /mg.fcgi/$1 [QSA,L]&lt;br /&gt;
&lt;br /&gt;
# Allow access to static and media directories&lt;br /&gt;
&amp;lt;Directory /path/to/mediagoblin/mediagoblin/static&amp;gt;&lt;br /&gt;
  Order allow,deny&lt;br /&gt;
  Allow from all&lt;br /&gt;
&amp;lt;/Directory&amp;gt;&lt;br /&gt;
&amp;lt;Directory /path/to/mediagoblin/mediagoblin/user_dev/media/public&amp;gt;&lt;br /&gt;
  Order allow,deny&lt;br /&gt;
  Allow from all&lt;br /&gt;
&amp;lt;/Directory&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# Connect to fcgi server&lt;br /&gt;
FastCGIExternalServer /var/www/mg.fcgi -host 127.0.0.1:26543&lt;br /&gt;
&amp;lt;/VirtualHost&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Then, you need to make sure mediagoblin is running in fcgi mode:&lt;br /&gt;
&amp;lt;pre&amp;gt;cd /path/to/mediagoblin&lt;br /&gt;
./lazyserver.sh --server-name=fcgi fcgi_host=127.0.0.1 fcgi_port=26543&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: there may be several ways to improve this configuration&lt;br /&gt;
&lt;br /&gt;
If it is too slow and you use the deflate module, you could try to use the following option : SetOutputFilter INFLATE&lt;br /&gt;
&lt;br /&gt;
= OpenShift =&lt;br /&gt;
&lt;br /&gt;
Thers&#039;s a blogpost saying how to [http://www.sud0.com/gnu-media-goblin-on-openshift.html install mediagoblin on openshift].&lt;br /&gt;
&lt;br /&gt;
= Juju =&lt;br /&gt;
&lt;br /&gt;
There is a juju [https://juju.ubuntu.com/Charms charm] available for deploying mediagoblin into EC2 or on your local box.  [https://juju.ubuntu.com/ juju] is available in Ubuntu 11.10 and later, though it is recommended that you pull it from [https://launchpad.net/~juju/+archive/pkgs the juju PPA], which includes backported packages going back to Ubuntu 11.10. To use the juju charm, install juju and configure either the [https://juju.ubuntu.com/docs/provider-configuration-local.html local provider] or one of the cloud API providers, currently the [https://juju.ubuntu.com/docs/provider-configuration-ec2.html EC2 provider] is the best supported and works not only with Amazon Web Services but also OpenStack clouds. There is also a newer native OpenStack API provider which is known to support HP Cloud.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# if you have not bootstrapped&lt;br /&gt;
juju bootstrap&lt;br /&gt;
mkdir ~/charms&lt;br /&gt;
bzr init-repo ~/charms/precise&lt;br /&gt;
bzr branch lp:~clint-fewbar/charms/precise/mediagoblin/trunk ~/charms/precise/mediagoblin&lt;br /&gt;
juju deploy --repository ~/charms local:mediagoblin&lt;br /&gt;
juju expose mediagoblin&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Currently the charm is volatile, deploying from trunk, and deploys a single-server version of MediaGoblin only. It will eventually relate to the existing juju charms for other supported data stores to allow one to scale out their MediaGoblin instance.&lt;br /&gt;
&lt;br /&gt;
= Init scripts =&lt;br /&gt;
&lt;br /&gt;
== Debian init scripts ==&lt;br /&gt;
&lt;br /&gt;
Joar has some scripts for running celery and mediagoblin separately that are designed for Debian.&lt;br /&gt;
&lt;br /&gt;
https://github.com/joar/mediagoblin-init-scripts&lt;br /&gt;
&lt;br /&gt;
== Arch Linux init scripts ==&lt;br /&gt;
&lt;br /&gt;
[http://whird.jpope.org/2012/04/14/mediagoblin-archlinux-rcd-scripts Jeremy Pope has written a nice blogpost] on how to add init scripts to deploy MediaGoblin with both the python paste http server and the celery deployments separated.&lt;br /&gt;
&lt;br /&gt;
If you want a simpler setup and don&#039;t want to deploy celery separately, consider either turning CELERY_ALWAYS_EAGER to true in the paste init script described above, or check out [http://chimo.chromic.org/2012/03/01/mediagoblin-init-script-on-archlinux/ Chimo&#039;s guide].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Generic, simple init script ==&lt;br /&gt;
&lt;br /&gt;
This is a super stupidly simple init script that was used for mediagoblin.com... note that this has Celery running in always eager mode.&lt;br /&gt;
&lt;br /&gt;
You will need to adjust the paths appropriately.  This could probably be better written!&lt;br /&gt;
&lt;br /&gt;
  #! /bin/sh&lt;br /&gt;
  &lt;br /&gt;
  ## Stupidly simple mediagoblin init script.&lt;br /&gt;
  # &lt;br /&gt;
  # Written in 2012 by Christopher Allan Webber&lt;br /&gt;
  #&lt;br /&gt;
  # To the extent possible under law, the author(s) have dedicated all&lt;br /&gt;
  # copyright and related and neighboring rights to this software to the&lt;br /&gt;
  # public domain worldwide. This software is distributed without any&lt;br /&gt;
  # warranty.&lt;br /&gt;
  # &lt;br /&gt;
  # You should have received a copy of the CC0 Public Domain Dedication along&lt;br /&gt;
  # with this software. If not, see&lt;br /&gt;
  # &amp;lt;http://creativecommons.org/publicdomain/zero/1.0/&amp;gt;.&lt;br /&gt;
  &lt;br /&gt;
  PASTER=/srv/mediagoblin.com/bin/paster&lt;br /&gt;
  PASTE_CONFIG=/srv/mediagoblin.com/paste.ini&lt;br /&gt;
  OPTIONS=&amp;quot;--pid-file=/tmp/mediagoblin.pid \&lt;br /&gt;
   --log-file=/srv/mediagoblin.com/paster.log \&lt;br /&gt;
   --server-name=fcgi fcgi_host=127.0.0.1 fcgi_port=26543&amp;quot;&lt;br /&gt;
  &lt;br /&gt;
  CELERY_ALWAYS_EAGER=true su -pc &amp;quot;$PASTER serve $PASTE_CONFIG $1 $OPTIONS&amp;quot; webadmin&lt;br /&gt;
&lt;br /&gt;
= Running on Dreamhost.com =&lt;br /&gt;
&lt;br /&gt;
===Set up your python virtualenv===&lt;br /&gt;
&lt;br /&gt;
dreamhost.com servers come with python 2.4 and 2.5 installed which is too old for mediagoblin. This means you need to compile and install a newer python (don&#039;t worry this is really not difficult on dreamhost servers). In order to be able to install python packages for a local user without touching system files, we need to setup a python &#039;&#039;virtualenv&#039;&#039;. Fortunately, this is not too tricky either. If your server has python 2.6 or 2.7 installed already, you can skip the [[#Install a local python|python installation stuff]] and you&#039;ll only need to [[#Setup virtualenv to install local python packages|install virtualenv]].&lt;br /&gt;
&lt;br /&gt;
====Install a local python====&lt;br /&gt;
# Download the latest python: http://python.org/ftp/python/XXX/Python-XXX.tar.bz2&lt;br /&gt;
# Unpack with &amp;lt;pre&amp;gt;tar xvjf Python-XXX.tar.bz2&amp;lt;/pre&amp;gt;&lt;br /&gt;
# &#039;&#039;cd&#039;&#039; into the directory and compile and install python locally:&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;pre&amp;gt;./configure --prefix=$HOME/local&lt;br /&gt;
make&lt;br /&gt;
make install&amp;lt;/pre&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
:This will install python (I used 2.7.3) into /home/&amp;lt;USERNAME&amp;gt;/local/bin/python. You might get warnings about some modules not being able to compile. :It was tcl/tk and bzip2 mainly, but things still worked out overall.&lt;br /&gt;
:&lt;br /&gt;
:You should now be able to invoke &amp;lt;tt&amp;gt;~/local/bin/python&amp;lt;/tt&amp;gt; and fall into a shell of your new python (exit with ctrl-d). Congrats, you have now a new python 2.7 that you can use. However, you will need to be able to install additional packages as a user and this is what virtualenv allows.&lt;br /&gt;
&lt;br /&gt;
====Setup virtualenv to install local python packages====&lt;br /&gt;
# Download the latest virtualenv: http://pypi.python.org/packages/source/v/virtualenv/XXX&lt;br /&gt;
# Install the virtualenv: &amp;lt;blockquote&amp;gt;&amp;lt;pre&amp;gt;~/local/bin/python ~/virtualenv-1.8/virtualenv.py  $HOME/local/virtenv&amp;lt;/pre&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
:: You will now have: ~/local/virtenv/bin/python&lt;br /&gt;
# In  ~/.bash_profile add: &amp;lt;blockquote&amp;gt;&amp;lt;pre&amp;gt;PATH=~/local/virtenv/bin:~/local/bin:${PATH}&amp;lt;/pre&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
:: so that your local python will be preferred.&lt;br /&gt;
:: Log out, log in again and test python&amp;quot; to see which version will be invoked. It should be the new python. Check &amp;quot;which easy_install&amp;quot; to see if the one in local/virtenv would be executed.&lt;br /&gt;
::  &lt;br /&gt;
:: You have now 1) a local &#039;&#039;python&#039;&#039; installation that you can use, and 2) &#039;&#039;easy_install&#039;&#039; will also work with your local user installation. From now on you can e.g. locally install the nose testing framework (&#039;&#039;easy_install nose&#039;&#039;) and use it (&#039;&#039;python -c &amp;quot;import nose&amp;quot;&#039;&#039;).&lt;br /&gt;
&lt;br /&gt;
===Install mediagoblin as a site package===&lt;br /&gt;
&lt;br /&gt;
# Check out mediagoblin from git to e.g. ~/mediagoblin&lt;br /&gt;
# Install MediaGoblin and all dependencies for MediaGoblin with easy_install.&lt;br /&gt;
:* In the mediagoblin directory issue:&lt;br /&gt;
:&amp;lt;blockquote&amp;gt;&amp;lt;pre&amp;gt;python setup.py&amp;lt;/pre&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
:* You will also need to: easy_install lxml&lt;br /&gt;
:* Python-image was trickier to install: &amp;lt;pre&amp;gt;easy_install --find-links http://www.pythonware.com/products/pil/ Imaging&amp;lt;/pre&amp;gt;&lt;br /&gt;
::Test by leaving the mediagoblin directory and see if you can import it: &amp;lt;pre&amp;gt;python -c &amp;quot;import mediagoblin&amp;quot;&amp;lt;/pre&amp;gt; looking for error messages.&lt;br /&gt;
&lt;br /&gt;
===Set up an WSGI environment on Dreamhost===&lt;br /&gt;
&lt;br /&gt;
# Enable the mod_passenger setting for ruby/python in the domains web panel&lt;br /&gt;
# &#039;&#039;cd&#039;&#039; into the domain directory (e.g. ~/media.sspaeth.de for me)&lt;br /&gt;
# Copy mediagoblin.ini and paste.ini from the ~/mediagoblin directory here&lt;br /&gt;
# Create passenger_wsgi.py in your domain directory (e.g. ~/media.sspaeth.de for me):&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&amp;lt;pre&amp;gt;import sys, os&lt;br /&gt;
INTERP = &amp;quot;/home/&amp;lt;username&amp;gt;/local/virtenv/bin/python&amp;quot;&lt;br /&gt;
#INTERP is present twice so that the new python interpreter knows the actual executable path&lt;br /&gt;
if sys.executable != INTERP: os.execl(INTERP, INTERP, *sys.argv)&lt;br /&gt;
&lt;br /&gt;
from paste.deploy import loadapp&lt;br /&gt;
application = loadapp(&#039;config:/home/mediagoblin/media.sspaeth.de/paste.ini&#039;)&lt;br /&gt;
&lt;br /&gt;
#If in case of errors, all you get are mysterious error 500, you can set debug=true in paste.ini to see stack traces&lt;br /&gt;
# Otherwise, add this:&lt;br /&gt;
#from paste.exceptions.errormiddleware import ErrorMiddleware&lt;br /&gt;
#application = ErrorMiddleware(application, debug=True)&amp;lt;/pre&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
# Set up the database by issueing: &amp;lt;pre&amp;gt;gmg dbupdate&amp;lt;/pre&amp;gt;&lt;br /&gt;
# (optional but recommended) Serve the static files directly from the webserver.&lt;br /&gt;
:In paste.ini in this section: [composite:routing] comment out the static files:&lt;br /&gt;
   #/mgoblin_static/ = mediagoblin_static&lt;br /&gt;
   #/theme_static/ = theme_static&lt;br /&gt;
:and symlink the mediagobin/mediagoblin/static directoy to public/mgoblin_static&lt;br /&gt;
:           and mediagoblin/mediagoblin/themes to public/theme_static&lt;br /&gt;
:&lt;br /&gt;
:so it is displayed directly. This step might be different depending on your web server configuration. There is no reason that static&lt;br /&gt;
:files need to go through all the python indirection when they can be served directly by nginx/apache/...&lt;br /&gt;
:&lt;br /&gt;
:In case you want to delete your git checkout after the installation (you don&#039;t need it, since you installed the mediagoblin package to ~/local/virtenv/lib/python2.7/mediagoblin... you should do the symlinking from there.&lt;br /&gt;
===Open Issues===&lt;br /&gt;
Please fill in the blanks if you find them out:&lt;br /&gt;
* How to enable logging to a file or standard html logs? Console output just disappears.&lt;br /&gt;
* How to set up things not using sqlite. What&#039;s wrong with mySQL?&lt;br /&gt;
&lt;br /&gt;
===Troubleshooting===&lt;br /&gt;
* First of all: invoke &amp;lt;pre&amp;gt;python passenger_wsgi.py&amp;lt;/pre&amp;gt; directly from the shell. This will tell you if there are import errors etc. If this command exits without any output on the console, you have already achieved a lot.&lt;br /&gt;
* In &#039;&#039;paste.ini&#039;&#039; set debug=true. This will show you python backtraces directly in the web page&lt;br /&gt;
* You will need to configure an smtp user/passwd/server in mediagoblin.ini, so you actually get the account creation token mailed out. Hint, the relevant settings are: &amp;lt;blockquote&amp;gt;&amp;lt;pre&amp;gt;email_debug_mode = false&lt;br /&gt;
email_sender_address = postmaster@sspaeth.de&lt;br /&gt;
email_smtp_host = SMTP.DOMAIN.TLD&lt;br /&gt;
email_smtp_user = USERNAME&lt;br /&gt;
email_smtp_pass = WEIRDPASSWORD&amp;lt;/pre&amp;gt;&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
* In case you can upload media, but it does not appear. You don&#039;t have the celery server running. Add &amp;lt;pre&amp;gt;CELERY_ALWAYS_EAGER = true&amp;lt;/pre&amp;gt; to the &#039;&#039;[celery]&#039;&#039; section in mediagoblin.ini&lt;br /&gt;
&lt;br /&gt;
=Miscellaneous Hacks=&lt;br /&gt;
==Force translation==&lt;br /&gt;
There might some conditions under which you would prefer that MediaGoblin always return the same translation.  If you are deploying with nginx and fastcgi, you can force MediaGoblin to return a specific translation regardless of browser preferences by passing a specific HTTP_ACCEPT_LANGUAGE fastcgi parameter in your location block. Example last line in location block:&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
    fastcgi_param HTTP_ACCEPT_LANGUAGE es; #force spanish translation&lt;br /&gt;
 &amp;lt;/nowiki&amp;gt;&lt;/div&gt;</summary>
		<author><name>Shackra</name></author>
	</entry>
	<entry>
		<id>https://wiki.mediagoblin.org/index.php?title=User:Shackra/Multimedia&amp;diff=754</id>
		<title>User:Shackra/Multimedia</title>
		<link rel="alternate" type="text/html" href="https://wiki.mediagoblin.org/index.php?title=User:Shackra/Multimedia&amp;diff=754"/>
		<updated>2012-05-27T07:42:23Z</updated>

		<summary type="html">&lt;p&gt;Shackra: /* Dependencies */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Video Support ==&lt;br /&gt;
&lt;br /&gt;
(from [[User:joar/Multimedia|Joar Multimedia]] page) Video and multimedia support has been merged into master. This does not mean that the information below is invalid, but that video support is considered more stable.&lt;br /&gt;
&lt;br /&gt;
== Dependencies ==&lt;br /&gt;
To enable video transcoding in MediaGoblin with [http://parabolagnulinux.org Parabola GNU/Linux-libre], you need to have the following packages:&lt;br /&gt;
&lt;br /&gt;
* python2-gtkhtml2&lt;br /&gt;
* gstreamer0.10-base-plugins&lt;br /&gt;
* gstreamer0.10-good-plugins&lt;br /&gt;
* gstreamer0.10-ugly-plugins&lt;br /&gt;
* gstreamer0.10-bad-libre-plugins&lt;br /&gt;
* gstreamer0.10-base&lt;br /&gt;
* gstreamer0.10-good&lt;br /&gt;
* gstreamer0.10-ugly&lt;br /&gt;
* gstreamer0.10-bad-libre&lt;br /&gt;
* gstreamer0.10-ffmpeg&lt;br /&gt;
* gstreamer0.10-python&lt;br /&gt;
* python2-gobject&lt;br /&gt;
&lt;br /&gt;
=== Installing dependencies ===&lt;br /&gt;
{{Cmd|pacman -S python2-gtkhtml2 gstreamer0.10-base-plugins gstreamer0.10-good-plugins gstreamer0.10-ugly-plugins gstreamer0.10-bad-libre-plugins gstreamer0.10-base gstreamer0.10-good gstreamer0.10-ugly gstreamer0.10-bad-libre gstreamer0.10-ffmpeg gstreamer0.10-python python2-gobject}}&lt;br /&gt;
&lt;br /&gt;
[http://cdn.memegenerator.net/instances/400x/21033858.jpg|With Parabola GNU/Linux-libre everything is so damn easy!]&lt;/div&gt;</summary>
		<author><name>Shackra</name></author>
	</entry>
	<entry>
		<id>https://wiki.mediagoblin.org/index.php?title=User:Shackra/Multimedia&amp;diff=753</id>
		<title>User:Shackra/Multimedia</title>
		<link rel="alternate" type="text/html" href="https://wiki.mediagoblin.org/index.php?title=User:Shackra/Multimedia&amp;diff=753"/>
		<updated>2012-05-27T07:32:55Z</updated>

		<summary type="html">&lt;p&gt;Shackra: /* Installing dependencies */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Video Support ==&lt;br /&gt;
&lt;br /&gt;
(from [[User:joar/Multimedia|Joar Multimedia]] page) Video and multimedia support has been merged into master. This does not mean that the information below is invalid, but that video support is considered more stable.&lt;br /&gt;
&lt;br /&gt;
== Dependencies ==&lt;br /&gt;
To enable video transcoding in MediaGoblin on [http://parabolagnulinux.org|Parabola GNU/Linux-libre], you need to have the following packages:&lt;br /&gt;
&lt;br /&gt;
* python2-gtkhtml2&lt;br /&gt;
* gstreamer0.10-base-plugins&lt;br /&gt;
* gstreamer0.10-good-plugins&lt;br /&gt;
* gstreamer0.10-ugly-plugins&lt;br /&gt;
* gstreamer0.10-bad-libre-plugins&lt;br /&gt;
* gstreamer0.10-base&lt;br /&gt;
* gstreamer0.10-good&lt;br /&gt;
* gstreamer0.10-ugly&lt;br /&gt;
* gstreamer0.10-bad-libre&lt;br /&gt;
* gstreamer0.10-ffmpeg&lt;br /&gt;
* gstreamer0.10-python&lt;br /&gt;
* python2-gobject&lt;br /&gt;
&lt;br /&gt;
=== Installing dependencies ===&lt;br /&gt;
{{Cmd|pacman -S python2-gtkhtml2 gstreamer0.10-base-plugins gstreamer0.10-good-plugins gstreamer0.10-ugly-plugins gstreamer0.10-bad-libre-plugins gstreamer0.10-base gstreamer0.10-good gstreamer0.10-ugly gstreamer0.10-bad-libre gstreamer0.10-ffmpeg gstreamer0.10-python python2-gobject}}&lt;br /&gt;
&lt;br /&gt;
[http://cdn.memegenerator.net/instances/400x/21033858.jpg|With Parabola GNU/Linux-libre everything is so damn easy!]&lt;/div&gt;</summary>
		<author><name>Shackra</name></author>
	</entry>
	<entry>
		<id>https://wiki.mediagoblin.org/index.php?title=User:Shackra/Multimedia&amp;diff=752</id>
		<title>User:Shackra/Multimedia</title>
		<link rel="alternate" type="text/html" href="https://wiki.mediagoblin.org/index.php?title=User:Shackra/Multimedia&amp;diff=752"/>
		<updated>2012-05-27T07:32:15Z</updated>

		<summary type="html">&lt;p&gt;Shackra: Created page with &amp;quot;== Video Support ==  (from Joar Multimedia page) Video and multimedia support has been merged into master. This does not mean that the information below ...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Video Support ==&lt;br /&gt;
&lt;br /&gt;
(from [[User:joar/Multimedia|Joar Multimedia]] page) Video and multimedia support has been merged into master. This does not mean that the information below is invalid, but that video support is considered more stable.&lt;br /&gt;
&lt;br /&gt;
== Dependencies ==&lt;br /&gt;
To enable video transcoding in MediaGoblin on [http://parabolagnulinux.org|Parabola GNU/Linux-libre], you need to have the following packages:&lt;br /&gt;
&lt;br /&gt;
* python2-gtkhtml2&lt;br /&gt;
* gstreamer0.10-base-plugins&lt;br /&gt;
* gstreamer0.10-good-plugins&lt;br /&gt;
* gstreamer0.10-ugly-plugins&lt;br /&gt;
* gstreamer0.10-bad-libre-plugins&lt;br /&gt;
* gstreamer0.10-base&lt;br /&gt;
* gstreamer0.10-good&lt;br /&gt;
* gstreamer0.10-ugly&lt;br /&gt;
* gstreamer0.10-bad-libre&lt;br /&gt;
* gstreamer0.10-ffmpeg&lt;br /&gt;
* gstreamer0.10-python&lt;br /&gt;
* python2-gobject&lt;br /&gt;
&lt;br /&gt;
=== Installing dependencies ===&lt;br /&gt;
{{Cmd|pacman -S python2-gtkhtml2 gstreamer0.10-base-plugins gstreamer0.10-good-plugins \&lt;br /&gt;
gstreamer0.10-ugly-plugins gstreamer0.10-bad-libre-plugins gstreamer0.10-base gstreamer0.10-good \ &lt;br /&gt;
gstreamer0.10-ugly gstreamer0.10-bad-libre gstreamer0.10-ffmpeg gstreamer0.10-python python2-gobject}}&lt;br /&gt;
&lt;br /&gt;
[http://cdn.memegenerator.net/instances/400x/21033858.jpg|With Parabola GNU/Linux-libre everything is so damn easy!]&lt;/div&gt;</summary>
		<author><name>Shackra</name></author>
	</entry>
	<entry>
		<id>https://wiki.mediagoblin.org/index.php?title=Feature_Ideas/Reprocessing&amp;diff=678</id>
		<title>Feature Ideas/Reprocessing</title>
		<link rel="alternate" type="text/html" href="https://wiki.mediagoblin.org/index.php?title=Feature_Ideas/Reprocessing&amp;diff=678"/>
		<updated>2012-04-20T06:03:44Z</updated>

		<summary type="html">&lt;p&gt;Shackra: what I said about this issue&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Rationale ==&lt;br /&gt;
&lt;br /&gt;
In MediaGoblin, processing refers to the act of transforming an original media file in various ways to make it suitable to serve.  For example, with images, we prepare resized versions for thumbnail and gallery views.  With video, we capture a thumbnail frame, and transcode a medium-sized version for embedded viewing.&lt;br /&gt;
&lt;br /&gt;
Normally, we process media as soon as we can after it&#039;s been uploaded to the site.  Sometimes, we want to reprocess some media.  There are a couple of reasons why this might happen:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;p&amp;gt;The original processing attempt failed.  This could be for lots of reasons: maybe a transcoding process was killed by a crazed sysadmin, or the file is corrupted, or there might even be a bug in MediaGoblin (crazy, I know!).&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Right now, when this happens, the unprocessed media lives in the database forever, a zombie.  Instead of that, we should periodically retry processing the media, when it makes sense.  Maybe we&#039;ll have better luck next time; if we do, it&#039;ll make the user happy.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Something has changed on the site such that we ought to reprocess media that has already been processed.  Maybe the administrator changed the size of thumbnail views, or in the future the MediaGoblin code will use a different audio codec.  For an event like this, we need to reprocess all the affected existing media to make sure we can effectively serve them in the new way.  These events should only take place when a site administrator requests it, and maybe when the site configuration changes to demand it.&lt;br /&gt;
&lt;br /&gt;
Brett plans to work on this.  If you want to help, get in touch!  This is [http://issues.mediagoblin.org/ticket/420 bug #420].&lt;br /&gt;
&lt;br /&gt;
== Reprocessing design ==&lt;br /&gt;
&lt;br /&gt;
=== When should we try to reprocess? ===&lt;br /&gt;
&lt;br /&gt;
If we&#039;re reprocessing media because previous attempts failed, we&#039;re likely to be more or less successful depending on &#039;&#039;why&#039;&#039; we failed.  If we failed because the machine was low on memory or disk at the time, reprocessing stands a good chance of succeeding.  If we failed because the media is corrupt, reprocessing will never work unless some code has changed in the meantime.&lt;br /&gt;
&lt;br /&gt;
TODO: We should collect known cases of when processing failed, what it looked like.  That will help us write code to determine why processing failed, and whether or not it&#039;s worthwhile to retry. (maybe this bug is a good start [http://issues.mediagoblin.org/ticket/438 bug#438] ?)&lt;br /&gt;
&lt;br /&gt;
=== When should we start reprocessing? ===&lt;br /&gt;
&lt;br /&gt;
There are two forces pushing us in different directions on this.  On the one hand, the more often we retry, the sooner the user&#039;s media will appear on the site, which makes them happy.  On the other hand, if we retry so often that not much can change between different attempts, we&#039;re just wasting computing resources to little end.  This could hurt our general site performance on deployments without resources to spare, like SheevaPlugs or Raspberry Pi systems.&lt;br /&gt;
&lt;br /&gt;
Like scheduling code in GNU/Linux, there are a million different ways we could approach this, and no one system is going to be perfect for every site.  We should instead strive to give hosts the tools they need to easily configure MediaGoblin according to their needs and their desires.&lt;br /&gt;
&lt;br /&gt;
The first piece of this puzzle is to make full use of Celery&#039;s task routing capabilities.  Each task should use an exchange that indicates at least:&lt;br /&gt;
&lt;br /&gt;
* the media type&lt;br /&gt;
* whether this processing is for&lt;br /&gt;
** a new upload&lt;br /&gt;
** retry after failed processing&lt;br /&gt;
** reprocessing at administrator request&lt;br /&gt;
&lt;br /&gt;
With this framework in place, a host has the capability to configure Celery with different worker pools for each of these exchanges depending on their needs and preferences.&lt;br /&gt;
&lt;br /&gt;
However, Celery doesn&#039;t handle scheduling of tasks outside the constraints of worker pools.  It&#039;s up to us to decide, and write in the code, issues like how long to wait between reprocessing attempts and when to give up completely.  (For version 1, I&#039;m planning an exponential backoff algorithm with a maximum wait of 1 day.  TODO: Should there be different configuration knobs for each media type?  That&#039;s a lot more complexity, but it&#039;s pretty hard to argue against the idea that expectations for processing ASCII art should be different from processing video.)  Key values should be stored in and read from the global MediaGoblin configuration.&lt;br /&gt;
&lt;br /&gt;
TODO: Discuss (at a meeting?) general priorities about how we want to balance &amp;quot;users see their media ASAP&amp;quot; vs. general site performance out of the box.  Get feedback about the version 1 scheme, and maybe get alternative proposals.&lt;br /&gt;
&lt;br /&gt;
=== cwebber&#039;s vague thoughts ===&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;09:09 &amp;lt; paroneayea&amp;gt; I&#039;ve been thinking vaguely about a few things related to &lt;br /&gt;
                    that like&lt;br /&gt;
09:10 &amp;lt; paroneayea&amp;gt; &amp;quot;what if you don&#039;t have the original anymore?  Does it &lt;br /&gt;
                    reprocess it into something more lossy?&amp;quot;&lt;br /&gt;
09:11 &amp;lt; paroneayea&amp;gt; &amp;quot;Should we set it up so that things can determine &lt;br /&gt;
                    conditionally if they should be reprocessed?  Ie, if &lt;br /&gt;
                    resolutions have changed, but this one was smaller than the &lt;br /&gt;
                    new lowest resolution anyway?&amp;quot;&lt;br /&gt;
09:11 &amp;lt; paroneayea&amp;gt; I&#039;m not sure what the answer to those are but I&#039;ve only &lt;br /&gt;
                    thought vaguely about them.&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Shackra said... ===&lt;br /&gt;
&lt;br /&gt;
Take in count the people using OpenStack for file storage. In case of re-encoding of audio files, for example, because MediaGoblin switched to ogg, will be impossible at least, if you deleted the original file and re-upload the new one.&lt;br /&gt;
&lt;br /&gt;
== Reprocessing implementation ==&lt;br /&gt;
&lt;br /&gt;
=== Base Task class ===&lt;br /&gt;
&lt;br /&gt;
I think we can write a common subclass of Celery&#039;s Task that will serve as the base for all of our processing tasks.  It would provide an &amp;lt;tt&amp;gt;on_failure&amp;lt;/tt&amp;gt; method to decide whether or not a retry is appropriate, and if so, handle rewriting the exchange (to mark this as a retry), calculate the exponential backoff time, and reschedule the task.&lt;br /&gt;
&lt;br /&gt;
This class could also serve as a unifying place to collect utility functions that many processing tasks need.  There&#039;s a lot of file handling code that&#039;s repeated with minor variation throughout the processing tasks right now; that could be abstracted into methods of this class to reduce redundancy in the code.&lt;br /&gt;
&lt;br /&gt;
=== Splitting up tasks ===&lt;br /&gt;
&lt;br /&gt;
Right now, our processing tasks are monolithic beasts: one single task performs all of the transformations necessary for the media to be considered &amp;quot;processed.&amp;quot;  We could improve code readability and maintainability, site reliability, and possibly even performance by splitting tasks up appropriately.&lt;br /&gt;
&lt;br /&gt;
The basic idea here is that each processing task would, after successful completion, queue up a &amp;quot;check if finished&amp;quot; task, which would in turn do quick checks to see if all the necessary results of processing are in place.  When it finds that they are, it marks the media entry as processed, and performs clean-up jobs like removing the original queued file, so that the media shows up in the gallery and so on.&lt;br /&gt;
&lt;br /&gt;
(Alternatively, the &amp;quot;check if finished&amp;quot; task could be more stateful, keeping track of which tasks fire it off, and then performing its own work when the last task reports in.  This approach seems more fragile and error-prone, so I prefer an approach that checks whether the subtasks actually did their jobs, but that might not always be possible, so I&#039;m making a note of this.)&lt;br /&gt;
&lt;br /&gt;
As an example: image processing includes four jobs: making a thumbnail, a medium image, stashing the original file (with slight renaming as appropriate), and saving EXIF and GPS data to the database.  These four tasks could each be run individually.  They all fire a &amp;quot;check if finished&amp;quot; task that examines if the media entry has files stashed from all these tasks (in other words, it peeks at &amp;lt;tt&amp;gt;media_files_dict&amp;lt;/tt&amp;gt;).  When all the files are in place, it marks the entry as processed, and performs necessary cleanup.&lt;br /&gt;
&lt;br /&gt;
We can potentially save a lot of work with this approach.  Consider a video where transcoding succeeds but generating a thumbnail fails.  By splitting tasks up, the resource-intensive transcoding will only run once, while we retry thumbnail generation appropriately.&lt;br /&gt;
&lt;br /&gt;
== User visibility ==&lt;br /&gt;
&lt;br /&gt;
After we have reprocessing code, logged in users should be able to see information about where their entries stand in the queue: it&#039;s going to be processed, it&#039;s going to be reprocessed by such-and-such time, it failed completely.  There are already some bugs about this (TODO: collect them here).  The current panel would be a good starting point for publishing this information generally.  There are also specific places where we could conditionally show useful information: for instance, mention around the media submission page that the media might be slow to appear if processing queues are unusually large.&lt;br /&gt;
&lt;br /&gt;
This is a big enough job that it could probably justify its own feature page...&lt;/div&gt;</summary>
		<author><name>Shackra</name></author>
	</entry>
	<entry>
		<id>https://wiki.mediagoblin.org/index.php?title=Feature_Ideas/Reprocessing&amp;diff=677</id>
		<title>Feature Ideas/Reprocessing</title>
		<link rel="alternate" type="text/html" href="https://wiki.mediagoblin.org/index.php?title=Feature_Ideas/Reprocessing&amp;diff=677"/>
		<updated>2012-04-20T05:53:02Z</updated>

		<summary type="html">&lt;p&gt;Shackra: Linux? WTF!&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Rationale ==&lt;br /&gt;
&lt;br /&gt;
In MediaGoblin, processing refers to the act of transforming an original media file in various ways to make it suitable to serve.  For example, with images, we prepare resized versions for thumbnail and gallery views.  With video, we capture a thumbnail frame, and transcode a medium-sized version for embedded viewing.&lt;br /&gt;
&lt;br /&gt;
Normally, we process media as soon as we can after it&#039;s been uploaded to the site.  Sometimes, we want to reprocess some media.  There are a couple of reasons why this might happen:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;p&amp;gt;The original processing attempt failed.  This could be for lots of reasons: maybe a transcoding process was killed by a crazed sysadmin, or the file is corrupted, or there might even be a bug in MediaGoblin (crazy, I know!).&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Right now, when this happens, the unprocessed media lives in the database forever, a zombie.  Instead of that, we should periodically retry processing the media, when it makes sense.  Maybe we&#039;ll have better luck next time; if we do, it&#039;ll make the user happy.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Something has changed on the site such that we ought to reprocess media that has already been processed.  Maybe the administrator changed the size of thumbnail views, or in the future the MediaGoblin code will use a different audio codec.  For an event like this, we need to reprocess all the affected existing media to make sure we can effectively serve them in the new way.  These events should only take place when a site administrator requests it, and maybe when the site configuration changes to demand it.&lt;br /&gt;
&lt;br /&gt;
Brett plans to work on this.  If you want to help, get in touch!  This is [http://issues.mediagoblin.org/ticket/420 bug #420].&lt;br /&gt;
&lt;br /&gt;
== Reprocessing design ==&lt;br /&gt;
&lt;br /&gt;
=== When should we try to reprocess? ===&lt;br /&gt;
&lt;br /&gt;
If we&#039;re reprocessing media because previous attempts failed, we&#039;re likely to be more or less successful depending on &#039;&#039;why&#039;&#039; we failed.  If we failed because the machine was low on memory or disk at the time, reprocessing stands a good chance of succeeding.  If we failed because the media is corrupt, reprocessing will never work unless some code has changed in the meantime.&lt;br /&gt;
&lt;br /&gt;
TODO: We should collect known cases of when processing failed, what it looked like.  That will help us write code to determine why processing failed, and whether or not it&#039;s worthwhile to retry. (maybe this bug is a good start [http://issues.mediagoblin.org/ticket/438 bug#438] ?)&lt;br /&gt;
&lt;br /&gt;
=== When should we start reprocessing? ===&lt;br /&gt;
&lt;br /&gt;
There are two forces pushing us in different directions on this.  On the one hand, the more often we retry, the sooner the user&#039;s media will appear on the site, which makes them happy.  On the other hand, if we retry so often that not much can change between different attempts, we&#039;re just wasting computing resources to little end.  This could hurt our general site performance on deployments without resources to spare, like SheevaPlugs or Raspberry Pi systems.&lt;br /&gt;
&lt;br /&gt;
Like scheduling code in GNU/Linux, there are a million different ways we could approach this, and no one system is going to be perfect for every site.  We should instead strive to give hosts the tools they need to easily configure MediaGoblin according to their needs and their desires.&lt;br /&gt;
&lt;br /&gt;
The first piece of this puzzle is to make full use of Celery&#039;s task routing capabilities.  Each task should use an exchange that indicates at least:&lt;br /&gt;
&lt;br /&gt;
* the media type&lt;br /&gt;
* whether this processing is for&lt;br /&gt;
** a new upload&lt;br /&gt;
** retry after failed processing&lt;br /&gt;
** reprocessing at administrator request&lt;br /&gt;
&lt;br /&gt;
With this framework in place, a host has the capability to configure Celery with different worker pools for each of these exchanges depending on their needs and preferences.&lt;br /&gt;
&lt;br /&gt;
However, Celery doesn&#039;t handle scheduling of tasks outside the constraints of worker pools.  It&#039;s up to us to decide, and write in the code, issues like how long to wait between reprocessing attempts and when to give up completely.  (For version 1, I&#039;m planning an exponential backoff algorithm with a maximum wait of 1 day.  TODO: Should there be different configuration knobs for each media type?  That&#039;s a lot more complexity, but it&#039;s pretty hard to argue against the idea that expectations for processing ASCII art should be different from processing video.)  Key values should be stored in and read from the global MediaGoblin configuration.&lt;br /&gt;
&lt;br /&gt;
TODO: Discuss (at a meeting?) general priorities about how we want to balance &amp;quot;users see their media ASAP&amp;quot; vs. general site performance out of the box.  Get feedback about the version 1 scheme, and maybe get alternative proposals.&lt;br /&gt;
&lt;br /&gt;
=== cwebber&#039;s vague thoughts ===&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;09:09 &amp;lt; paroneayea&amp;gt; I&#039;ve been thinking vaguely about a few things related to &lt;br /&gt;
                    that like&lt;br /&gt;
09:10 &amp;lt; paroneayea&amp;gt; &amp;quot;what if you don&#039;t have the original anymore?  Does it &lt;br /&gt;
                    reprocess it into something more lossy?&amp;quot;&lt;br /&gt;
09:11 &amp;lt; paroneayea&amp;gt; &amp;quot;Should we set it up so that things can determine &lt;br /&gt;
                    conditionally if they should be reprocessed?  Ie, if &lt;br /&gt;
                    resolutions have changed, but this one was smaller than the &lt;br /&gt;
                    new lowest resolution anyway?&amp;quot;&lt;br /&gt;
09:11 &amp;lt; paroneayea&amp;gt; I&#039;m not sure what the answer to those are but I&#039;ve only &lt;br /&gt;
                    thought vaguely about them.&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Reprocessing implementation ==&lt;br /&gt;
&lt;br /&gt;
=== Base Task class ===&lt;br /&gt;
&lt;br /&gt;
I think we can write a common subclass of Celery&#039;s Task that will serve as the base for all of our processing tasks.  It would provide an &amp;lt;tt&amp;gt;on_failure&amp;lt;/tt&amp;gt; method to decide whether or not a retry is appropriate, and if so, handle rewriting the exchange (to mark this as a retry), calculate the exponential backoff time, and reschedule the task.&lt;br /&gt;
&lt;br /&gt;
This class could also serve as a unifying place to collect utility functions that many processing tasks need.  There&#039;s a lot of file handling code that&#039;s repeated with minor variation throughout the processing tasks right now; that could be abstracted into methods of this class to reduce redundancy in the code.&lt;br /&gt;
&lt;br /&gt;
=== Splitting up tasks ===&lt;br /&gt;
&lt;br /&gt;
Right now, our processing tasks are monolithic beasts: one single task performs all of the transformations necessary for the media to be considered &amp;quot;processed.&amp;quot;  We could improve code readability and maintainability, site reliability, and possibly even performance by splitting tasks up appropriately.&lt;br /&gt;
&lt;br /&gt;
The basic idea here is that each processing task would, after successful completion, queue up a &amp;quot;check if finished&amp;quot; task, which would in turn do quick checks to see if all the necessary results of processing are in place.  When it finds that they are, it marks the media entry as processed, and performs clean-up jobs like removing the original queued file, so that the media shows up in the gallery and so on.&lt;br /&gt;
&lt;br /&gt;
(Alternatively, the &amp;quot;check if finished&amp;quot; task could be more stateful, keeping track of which tasks fire it off, and then performing its own work when the last task reports in.  This approach seems more fragile and error-prone, so I prefer an approach that checks whether the subtasks actually did their jobs, but that might not always be possible, so I&#039;m making a note of this.)&lt;br /&gt;
&lt;br /&gt;
As an example: image processing includes four jobs: making a thumbnail, a medium image, stashing the original file (with slight renaming as appropriate), and saving EXIF and GPS data to the database.  These four tasks could each be run individually.  They all fire a &amp;quot;check if finished&amp;quot; task that examines if the media entry has files stashed from all these tasks (in other words, it peeks at &amp;lt;tt&amp;gt;media_files_dict&amp;lt;/tt&amp;gt;).  When all the files are in place, it marks the entry as processed, and performs necessary cleanup.&lt;br /&gt;
&lt;br /&gt;
We can potentially save a lot of work with this approach.  Consider a video where transcoding succeeds but generating a thumbnail fails.  By splitting tasks up, the resource-intensive transcoding will only run once, while we retry thumbnail generation appropriately.&lt;br /&gt;
&lt;br /&gt;
== User visibility ==&lt;br /&gt;
&lt;br /&gt;
After we have reprocessing code, logged in users should be able to see information about where their entries stand in the queue: it&#039;s going to be processed, it&#039;s going to be reprocessed by such-and-such time, it failed completely.  There are already some bugs about this (TODO: collect them here).  The current panel would be a good starting point for publishing this information generally.  There are also specific places where we could conditionally show useful information: for instance, mention around the media submission page that the media might be slow to appear if processing queues are unusually large.&lt;br /&gt;
&lt;br /&gt;
This is a big enough job that it could probably justify its own feature page...&lt;/div&gt;</summary>
		<author><name>Shackra</name></author>
	</entry>
	<entry>
		<id>https://wiki.mediagoblin.org/index.php?title=Feature_Ideas/Reprocessing&amp;diff=676</id>
		<title>Feature Ideas/Reprocessing</title>
		<link rel="alternate" type="text/html" href="https://wiki.mediagoblin.org/index.php?title=Feature_Ideas/Reprocessing&amp;diff=676"/>
		<updated>2012-04-20T05:51:39Z</updated>

		<summary type="html">&lt;p&gt;Shackra: adding a possible bug to start&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Rationale ==&lt;br /&gt;
&lt;br /&gt;
In MediaGoblin, processing refers to the act of transforming an original media file in various ways to make it suitable to serve.  For example, with images, we prepare resized versions for thumbnail and gallery views.  With video, we capture a thumbnail frame, and transcode a medium-sized version for embedded viewing.&lt;br /&gt;
&lt;br /&gt;
Normally, we process media as soon as we can after it&#039;s been uploaded to the site.  Sometimes, we want to reprocess some media.  There are a couple of reasons why this might happen:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;p&amp;gt;The original processing attempt failed.  This could be for lots of reasons: maybe a transcoding process was killed by a crazed sysadmin, or the file is corrupted, or there might even be a bug in MediaGoblin (crazy, I know!).&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Right now, when this happens, the unprocessed media lives in the database forever, a zombie.  Instead of that, we should periodically retry processing the media, when it makes sense.  Maybe we&#039;ll have better luck next time; if we do, it&#039;ll make the user happy.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Something has changed on the site such that we ought to reprocess media that has already been processed.  Maybe the administrator changed the size of thumbnail views, or in the future the MediaGoblin code will use a different audio codec.  For an event like this, we need to reprocess all the affected existing media to make sure we can effectively serve them in the new way.  These events should only take place when a site administrator requests it, and maybe when the site configuration changes to demand it.&lt;br /&gt;
&lt;br /&gt;
Brett plans to work on this.  If you want to help, get in touch!  This is [http://issues.mediagoblin.org/ticket/420 bug #420].&lt;br /&gt;
&lt;br /&gt;
== Reprocessing design ==&lt;br /&gt;
&lt;br /&gt;
=== When should we try to reprocess? ===&lt;br /&gt;
&lt;br /&gt;
If we&#039;re reprocessing media because previous attempts failed, we&#039;re likely to be more or less successful depending on &#039;&#039;why&#039;&#039; we failed.  If we failed because the machine was low on memory or disk at the time, reprocessing stands a good chance of succeeding.  If we failed because the media is corrupt, reprocessing will never work unless some code has changed in the meantime.&lt;br /&gt;
&lt;br /&gt;
TODO: We should collect known cases of when processing failed, what it looked like.  That will help us write code to determine why processing failed, and whether or not it&#039;s worthwhile to retry. (maybe this bug is a good start [http://issues.mediagoblin.org/ticket/438 bug#438] ?)&lt;br /&gt;
&lt;br /&gt;
=== When should we start reprocessing? ===&lt;br /&gt;
&lt;br /&gt;
There are two forces pushing us in different directions on this.  On the one hand, the more often we retry, the sooner the user&#039;s media will appear on the site, which makes them happy.  On the other hand, if we retry so often that not much can change between different attempts, we&#039;re just wasting computing resources to little end.  This could hurt our general site performance on deployments without resources to spare, like SheevaPlugs or Raspberry Pi systems.&lt;br /&gt;
&lt;br /&gt;
Like scheduling code in Linux, there are a million different ways we could approach this, and no one system is going to be perfect for every site.  We should instead strive to give hosts the tools they need to easily configure MediaGoblin according to their needs and their desires.&lt;br /&gt;
&lt;br /&gt;
The first piece of this puzzle is to make full use of Celery&#039;s task routing capabilities.  Each task should use an exchange that indicates at least:&lt;br /&gt;
&lt;br /&gt;
* the media type&lt;br /&gt;
* whether this processing is for&lt;br /&gt;
** a new upload&lt;br /&gt;
** retry after failed processing&lt;br /&gt;
** reprocessing at administrator request&lt;br /&gt;
&lt;br /&gt;
With this framework in place, a host has the capability to configure Celery with different worker pools for each of these exchanges depending on their needs and preferences.&lt;br /&gt;
&lt;br /&gt;
However, Celery doesn&#039;t handle scheduling of tasks outside the constraints of worker pools.  It&#039;s up to us to decide, and write in the code, issues like how long to wait between reprocessing attempts and when to give up completely.  (For version 1, I&#039;m planning an exponential backoff algorithm with a maximum wait of 1 day.  TODO: Should there be different configuration knobs for each media type?  That&#039;s a lot more complexity, but it&#039;s pretty hard to argue against the idea that expectations for processing ASCII art should be different from processing video.)  Key values should be stored in and read from the global MediaGoblin configuration.&lt;br /&gt;
&lt;br /&gt;
TODO: Discuss (at a meeting?) general priorities about how we want to balance &amp;quot;users see their media ASAP&amp;quot; vs. general site performance out of the box.  Get feedback about the version 1 scheme, and maybe get alternative proposals.&lt;br /&gt;
&lt;br /&gt;
=== cwebber&#039;s vague thoughts ===&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;09:09 &amp;lt; paroneayea&amp;gt; I&#039;ve been thinking vaguely about a few things related to &lt;br /&gt;
                    that like&lt;br /&gt;
09:10 &amp;lt; paroneayea&amp;gt; &amp;quot;what if you don&#039;t have the original anymore?  Does it &lt;br /&gt;
                    reprocess it into something more lossy?&amp;quot;&lt;br /&gt;
09:11 &amp;lt; paroneayea&amp;gt; &amp;quot;Should we set it up so that things can determine &lt;br /&gt;
                    conditionally if they should be reprocessed?  Ie, if &lt;br /&gt;
                    resolutions have changed, but this one was smaller than the &lt;br /&gt;
                    new lowest resolution anyway?&amp;quot;&lt;br /&gt;
09:11 &amp;lt; paroneayea&amp;gt; I&#039;m not sure what the answer to those are but I&#039;ve only &lt;br /&gt;
                    thought vaguely about them.&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Reprocessing implementation ==&lt;br /&gt;
&lt;br /&gt;
=== Base Task class ===&lt;br /&gt;
&lt;br /&gt;
I think we can write a common subclass of Celery&#039;s Task that will serve as the base for all of our processing tasks.  It would provide an &amp;lt;tt&amp;gt;on_failure&amp;lt;/tt&amp;gt; method to decide whether or not a retry is appropriate, and if so, handle rewriting the exchange (to mark this as a retry), calculate the exponential backoff time, and reschedule the task.&lt;br /&gt;
&lt;br /&gt;
This class could also serve as a unifying place to collect utility functions that many processing tasks need.  There&#039;s a lot of file handling code that&#039;s repeated with minor variation throughout the processing tasks right now; that could be abstracted into methods of this class to reduce redundancy in the code.&lt;br /&gt;
&lt;br /&gt;
=== Splitting up tasks ===&lt;br /&gt;
&lt;br /&gt;
Right now, our processing tasks are monolithic beasts: one single task performs all of the transformations necessary for the media to be considered &amp;quot;processed.&amp;quot;  We could improve code readability and maintainability, site reliability, and possibly even performance by splitting tasks up appropriately.&lt;br /&gt;
&lt;br /&gt;
The basic idea here is that each processing task would, after successful completion, queue up a &amp;quot;check if finished&amp;quot; task, which would in turn do quick checks to see if all the necessary results of processing are in place.  When it finds that they are, it marks the media entry as processed, and performs clean-up jobs like removing the original queued file, so that the media shows up in the gallery and so on.&lt;br /&gt;
&lt;br /&gt;
(Alternatively, the &amp;quot;check if finished&amp;quot; task could be more stateful, keeping track of which tasks fire it off, and then performing its own work when the last task reports in.  This approach seems more fragile and error-prone, so I prefer an approach that checks whether the subtasks actually did their jobs, but that might not always be possible, so I&#039;m making a note of this.)&lt;br /&gt;
&lt;br /&gt;
As an example: image processing includes four jobs: making a thumbnail, a medium image, stashing the original file (with slight renaming as appropriate), and saving EXIF and GPS data to the database.  These four tasks could each be run individually.  They all fire a &amp;quot;check if finished&amp;quot; task that examines if the media entry has files stashed from all these tasks (in other words, it peeks at &amp;lt;tt&amp;gt;media_files_dict&amp;lt;/tt&amp;gt;).  When all the files are in place, it marks the entry as processed, and performs necessary cleanup.&lt;br /&gt;
&lt;br /&gt;
We can potentially save a lot of work with this approach.  Consider a video where transcoding succeeds but generating a thumbnail fails.  By splitting tasks up, the resource-intensive transcoding will only run once, while we retry thumbnail generation appropriately.&lt;br /&gt;
&lt;br /&gt;
== User visibility ==&lt;br /&gt;
&lt;br /&gt;
After we have reprocessing code, logged in users should be able to see information about where their entries stand in the queue: it&#039;s going to be processed, it&#039;s going to be reprocessed by such-and-such time, it failed completely.  There are already some bugs about this (TODO: collect them here).  The current panel would be a good starting point for publishing this information generally.  There are also specific places where we could conditionally show useful information: for instance, mention around the media submission page that the media might be slow to appear if processing queues are unusually large.&lt;br /&gt;
&lt;br /&gt;
This is a big enough job that it could probably justify its own feature page...&lt;/div&gt;</summary>
		<author><name>Shackra</name></author>
	</entry>
</feed>