diff options
140 files changed, 9593 insertions, 3277 deletions
@@ -9,19 +9,27 @@ /lib/ /include/ /parts/ +/share/ /mediagoblin.egg-info /docs/_build/ /docs/build /user_dev/ /paste_local.ini /mediagoblin_local.ini +/mediagoblin.db +/celery.db +/kombu.db /server-log.txt +/mediagoblin/db/sql_switch.py # Tests /mediagoblin/tests/user_dev/ -.installed.cfg +# File extensions *.pyc *.pyo *~ *.swp + +# The legacy of buildout +.installed.cfg
\ No newline at end of file @@ -30,7 +30,7 @@ If not, see <http://www.gnu.org/licenses/>. JavaScript files located in the ``mediagoblin/`` directory tree are free software: you can redistribute and/or modify them under the -terms of the GNU Lesser General Public License as published by the +terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. diff --git a/docs/source/foreword.rst b/docs/source/foreword.rst index be0c84b8..39ece25d 100644 --- a/docs/source/foreword.rst +++ b/docs/source/foreword.rst @@ -28,7 +28,7 @@ We have other documentation at: * http://wiki.mediagoblin.org/ for our contributor/developer-focused wiki -Improving the MediaGobiin Manual +Improving the MediaGoblin Manual ================================ There are a few ways---please pick whichever method is convenient for diff --git a/extlib/video-js/demo.html b/extlib/video-js/demo.html new file mode 100644 index 00000000..a8393af0 --- /dev/null +++ b/extlib/video-js/demo.html @@ -0,0 +1,23 @@ +<!DOCTYPE html> +<html> +<head> + <title>Video.js | HTML5 Video Player</title> + + <link href="http://vjs.zencdn.net/c/video-js.css" rel="stylesheet" type="text/css"> + + <!-- video.js must be in the <head> for older IEs to work. --> + <script src="http://vjs.zencdn.net/c/video.js"></script> + +</head> +<body> + + <video id="example_video_1" class="video-js vjs-default-skin" controls preload="none" width="640" height="264" + poster="http://video-js.zencoder.com/oceans-clip.png" + data-setup="{}"> + <source src="http://video-js.zencoder.com/oceans-clip.mp4" type='video/mp4' /> + <source src="http://video-js.zencoder.com/oceans-clip.webm" type='video/webm' /> + <source src="http://video-js.zencoder.com/oceans-clip.ogv" type='video/ogg' /> + </video> + +</body> +</html> diff --git a/extlib/video-js/video-js.css b/extlib/video-js/video-js.css new file mode 100644 index 00000000..a1a18a00 --- /dev/null +++ b/extlib/video-js/video-js.css @@ -0,0 +1,427 @@ +/* +VideoJS Default Styles (http://videojs.com) +Version 3.1.0 +*/ + +/* +REQUIRED STYLES (be careful overriding) +================================================================================ */ +/* When loading the player, the video tag is replaced with a DIV, + that will hold the video tag or object tag for other playback methods. + The div contains the video playback element (Flash or HTML5) and controls, and sets the width and height of the video. + + ** If you want to add some kind of border/padding (e.g. a frame), or special positioning, use another containing element. + Otherwise you risk messing up control positioning and full window mode. ** +*/ +.video-js { + background-color: #000; position: relative; padding: 0; + + /* Start with 10px for base font size so other dimensions can be em based and easily calculable. */ + font-size: 10px; + + /* Allow poster to be vertially aligned. */ + vertical-align: middle; + /* display: table-cell; */ /*This works in Safari but not Firefox.*/ +} + +/* Playback technology elements expand to the width/height of the containing div. <video> or <object> */ +.video-js .vjs-tech { position: absolute; top: 0; left: 0; width: 100%; height: 100%; } + +/* Fix for Firefox 9 fullscreen (only if it is enabled). Not needed when checking fullScreenEnabled. */ +.video-js:-moz-full-screen { position: absolute; } + +/* Fullscreen Styles */ +body.vjs-full-window { + padding: 0; margin: 0; + height: 100%; overflow-y: auto; /* Fix for IE6 full-window. http://www.cssplay.co.uk/layouts/fixed.html */ +} +.video-js.vjs-fullscreen { + position: fixed; overflow: hidden; z-index: 1000; left: 0; top: 0; bottom: 0; right: 0; width: 100% !important; height: 100% !important; + _position: absolute; /* IE6 Full-window (underscore hack) */ +} +.video-js:-webkit-full-screen { + width: 100% !important; height: 100% !important; +} + +/* Poster Styles */ +.vjs-poster { + margin: 0 auto; padding: 0; cursor: pointer; + + /* Scale with the size of the player div. Works when poster is vertically shorter, but stretches when it's less wide. */ + position: relative; width: 100%; max-height: 100%; +} + +/* Subtiles Styles */ +.video-js .vjs-subtitles { color: #fff; font-size: 20px; text-align: center; position: absolute; bottom: 40px; left: 0; right: 0; } + +/* Fading sytles, used to fade control bar. */ +.vjs-fade-in { + visibility: visible !important; /* Needed to make sure things hide in older browsers too. */ + opacity: 1 !important; + + -webkit-transition: visibility 0s linear 0s, opacity 0.3s linear; + -moz-transition: visibility 0s linear 0s, opacity 0.3s linear; + -ms-transition: visibility 0s linear 0s, opacity 0.3s linear; + -o-transition: visibility 0s linear 0s, opacity 0.3s linear; + transition: visibility 0s linear 0s, opacity 0.3s linear; +} +.vjs-fade-out { + visibility: hidden !important; + opacity: 0 !important; + + -webkit-transition: visibility 0s linear 1.5s,opacity 1.5s linear; + -moz-transition: visibility 0s linear 1.5s,opacity 1.5s linear; + -ms-transition: visibility 0s linear 1.5s,opacity 1.5s linear; + -o-transition: visibility 0s linear 1.5s,opacity 1.5s linear; + transition: visibility 0s linear 1.5s,opacity 1.5s linear; +} + +/* DEFAULT SKIN (override in another file to create new skins) +================================================================================ +Instead of editing this file, I recommend creating your own skin CSS file to be included after this file, +so you can upgrade to newer versions easier. You can remove all these styles by removing the 'vjs-default-skin' class from the tag. */ + +/* The default control bar. Created by bar.js */ +.vjs-default-skin .vjs-controls { + position: absolute; + bottom: 0; /* Distance from the bottom of the box/video. Keep 0. Use height to add more bottom margin. */ + left: 0; right: 0; /* 100% width of div */ + margin: 0; padding: 0; /* Controls are absolutely position, so no padding necessary */ + height: 2.6em; /* Including any margin you want above or below control items */ + color: #fff; border-top: 1px solid #404040; + + /* CSS Gradient */ + /* Can use the Ultimate CSS Gradient Generator: http://www.colorzilla.com/gradient-editor/ */ + background: #242424; /* Old browsers */ + background: -moz-linear-gradient(top, #242424 50%, #1f1f1f 50%, #171717 100%); /* FF3.6+ */ + background: -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(50%,#242424), color-stop(50%,#1f1f1f), color-stop(100%,#171717)); /* Chrome,Safari4+ */ + background: -webkit-linear-gradient(top, #242424 50%,#1f1f1f 50%,#171717 100%); /* Chrome10+,Safari5.1+ */ + background: -o-linear-gradient(top, #242424 50%,#1f1f1f 50%,#171717 100%); /* Opera11.10+ */ + background: -ms-linear-gradient(top, #242424 50%,#1f1f1f 50%,#171717 100%); /* IE10+ */ + /* Filter was causing a lot of weird issues in IE. Elements would stop showing up, or other styles would break. */ + /*filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#242424', endColorstr='#171717',GradientType=0 );*/ /* IE6-9 */ + background: linear-gradient(top, #242424 50%,#1f1f1f 50%,#171717 100%); /* W3C */ + + /* Start hidden and with 0 opacity. Opacity is used to fade in modern browsers. */ + /* Can't use display block to hide initially because widths of slider handles aren't calculated and avaialbe for positioning correctly. */ + visibility: hidden; + opacity: 0; +} + +/* General styles for individual controls. */ +.vjs-default-skin .vjs-control { + position: relative; float: left; + text-align: center; margin: 0; padding: 0; + height: 2.6em; width: 2.6em; +} + +.vjs-default-skin .vjs-control:focus { + outline: 0; +} + +/* Hide control text visually, but have it available for screenreaders: h5bp.com/v */ +.vjs-default-skin .vjs-control-text { border: 0; clip: rect(0 0 0 0); height: 1px; margin: -1px; overflow: hidden; padding: 0; position: absolute; width: 1px; } + + +/* Play/Pause +-------------------------------------------------------------------------------- */ +.vjs-default-skin .vjs-play-control { width: 5em; cursor: pointer !important; } +/* Play Icon */ +.vjs-default-skin.vjs-paused .vjs-play-control div { width: 15px; height: 17px; background: url('video-js.png'); margin: 0.5em auto 0; } +.vjs-default-skin.vjs-playing .vjs-play-control div { width: 15px; height: 17px; background: url('video-js.png') -25px 0; margin: 0.5em auto 0; } + +/* Rewind +-------------------------------------------------------------------------------- */ +.vjs-default-skin .vjs-rewind-control { width: 5em; cursor: pointer !important; } +.vjs-default-skin .vjs-rewind-control div { width: 19px; height: 16px; background: url('video-js.png'); margin: 0.5em auto 0; } + +/* Volume/Mute +-------------------------------------------------------------------------------- */ +.vjs-default-skin .vjs-mute-control { width: 3.8em; cursor: pointer !important; float: right; } +.vjs-default-skin .vjs-mute-control div { width: 22px; height: 16px; background: url('video-js.png') -75px -25px; margin: 0.5em auto 0; } +.vjs-default-skin .vjs-mute-control.vjs-vol-0 div { background: url('video-js.png') 0 -25px; } +.vjs-default-skin .vjs-mute-control.vjs-vol-1 div { background: url('video-js.png') -25px -25px; } +.vjs-default-skin .vjs-mute-control.vjs-vol-2 div { background: url('video-js.png') -50px -25px; } + + +.vjs-default-skin .vjs-volume-control { width: 5em; float: right; } +.vjs-default-skin .vjs-volume-bar { + position: relative; width: 5em; height: 0.6em; margin: 1em auto 0; cursor: pointer !important; + + -moz-border-radius: 0.3em; -webkit-border-radius: 0.3em; border-radius: 0.3em; + + background: #666; + background: -moz-linear-gradient(top, #333, #666); + background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#333), to(#666)); + background: -webkit-linear-gradient(top, #333, #666); + background: -o-linear-gradient(top, #333, #666); + background: -ms-linear-gradient(top, #333, #666); + background: linear-gradient(top, #333, #666); +} +.vjs-default-skin .vjs-volume-level { + position: absolute; top: 0; left: 0; height: 0.6em; + + -moz-border-radius: 0.3em; -webkit-border-radius: 0.3em; border-radius: 0.3em; + + background: #fff; + background: -moz-linear-gradient(top, #fff, #ccc); + background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#fff), to(#ccc)); + background: -webkit-linear-gradient(top, #fff, #ccc); + background: -o-linear-gradient(top, #fff, #ccc); + background: -ms-linear-gradient(top, #fff, #ccc); + background: linear-gradient(top, #fff, #ccc); +} +.vjs-default-skin .vjs-volume-handle { + position: absolute; top: -0.2em; width: 0.8em; height: 0.8em; background: #ccc; left: 0; + border: 1px solid #fff; + -moz-border-radius: 0.6em; -webkit-border-radius: 0.6em; border-radius: 0.6em; +} + +/* Progress +-------------------------------------------------------------------------------- */ +.vjs-default-skin div.vjs-progress-control { + position: absolute; + left: 4.8em; right: 4.8em; /* Leave room for time displays. */ + height: 1.0em; width: auto; + top: -1.3em; /* Set above the rest of the controls. And leave room for 2px of borders (progress bottom and controls top). */ + border-bottom: 1px solid #1F1F1F; + border-top: 1px solid #222; + + /* CSS Gradient */ + background: #333; + background: -moz-linear-gradient(top, #222, #333); + background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#222), to(#333)); + background: -webkit-linear-gradient(top, #222, #333); + background: -o-linear-gradient(top, #333, #222); + background: -ms-linear-gradient(top, #333, #222); + background: linear-gradient(top, #333, #222); + + + /* 1px top shadow */ +/* -webkit-box-shadow: 0px -1px 0px 0px rgba(0, 0, 0, 0.15); -moz-box-shadow: 0px -1px 0px 0px rgba(0, 0, 0, 0.15); box-shadow: 0px -1px 0px 0px rgba(0, 0, 0, 0.15);*/ +} + +/* Box containing play and load progresses. Also acts as seek scrubber. */ +.vjs-default-skin .vjs-progress-holder { + position: relative; cursor: pointer !important; /*overflow: hidden;*/ + padding: 0; margin: 0; /* Placement within the progress control item */ + height: 1.0em; + -moz-border-radius: 0.6em; -webkit-border-radius: 0.6em; border-radius: 0.6em; + + /* CSS Gradient */ + background: #111; + background: -moz-linear-gradient(top, #111, #262626); + background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#111), to(#262626)); + background: -webkit-linear-gradient(top, #111, #262626); + background: -o-linear-gradient(top, #111, #262626); + background: -ms-linear-gradient(top, #111, #262626); + background: linear-gradient(top, #111, #262626); +} +.vjs-default-skin .vjs-progress-holder .vjs-play-progress, +.vjs-default-skin .vjs-progress-holder .vjs-load-progress { /* Progress Bars */ + position: absolute; display: block; height: 1.0em; margin: 0; padding: 0; + left: 0; top: 0; /*Needed for IE6*/ + -moz-border-radius: 0.6em; -webkit-border-radius: 0.6em; border-radius: 0.6em; + + /*width: 0;*/ +} + +.vjs-default-skin .vjs-play-progress { + /* CSS Gradient. */ + background: #fff; /* Old browsers */ + background: -moz-linear-gradient(top, #fff 0%, #d6d6d6 50%, #fff 100%); + background: -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(0%,#fff), color-stop(50%,#d6d6d6), color-stop(100%,#fff)); + background: -webkit-linear-gradient(top, #fff 0%,#d6d6d6 50%,#fff 100%); + background: -o-linear-gradient(top, #fff 0%,#d6d6d6 50%,#fff 100%); + background: -ms-linear-gradient(top, #fff 0%,#d6d6d6 50%,#fff 100%); + background: linear-gradient(top, #fff 0%,#d6d6d6 50%,#fff 100%); + + background: #efefef; + background: -moz-linear-gradient(top, #efefef 0%, #f5f5f5 50%, #dbdbdb 50%, #f1f1f1 100%); + background: -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(0%,#efefef), color-stop(50%,#f5f5f5), color-stop(50%,#dbdbdb), color-stop(100%,#f1f1f1)); + background: -webkit-linear-gradient(top, #efefef 0%,#f5f5f5 50%,#dbdbdb 50%,#f1f1f1 100%); + background: -o-linear-gradient(top, #efefef 0%,#f5f5f5 50%,#dbdbdb 50%,#f1f1f1 100%); + background: -ms-linear-gradient(top, #efefef 0%,#f5f5f5 50%,#dbdbdb 50%,#f1f1f1 100%); + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#efefef', endColorstr='#f1f1f1',GradientType=0 ); + background: linear-gradient(top, #efefef 0%,#f5f5f5 50%,#dbdbdb 50%,#f1f1f1 100%); +} +.vjs-default-skin .vjs-load-progress { + opacity: 0.8; + + /* CSS Gradient */ + background: #666; + background: -moz-linear-gradient(top, #666, #333); + background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#666), to(#333)); + background: -webkit-linear-gradient(top, #666, #333); + background: -o-linear-gradient(top, #666, #333); + background: -ms-linear-gradient(top, #666, #333); + background: linear-gradient(top, #666, #333); +} + +.vjs-default-skin div.vjs-seek-handle { + position: absolute; + width: 16px; height: 16px; /* Match img pixles */ + margin-top: -0.3em; + left: 0; top: 0; /*Needed for IE6*/ + + background: url('video-js.png') 0 -50px; + /* CSS Curved Corners. Needed to make shadows curved. */ + -moz-border-radius: 0.8em; -webkit-border-radius: 0.8em; border-radius: 0.8em; + /* CSS Shadows */ + -webkit-box-shadow: 0 2px 4px 0 #000; -moz-box-shadow: 0 2px 4px 0 #000; box-shadow: 0 2px 4px 0 #000; +} +/* Time Display +-------------------------------------------------------------------------------- */ +.vjs-default-skin .vjs-time-controls { + position: absolute; + right: 0; + height: 1.0em; width: 4.8em; + top: -1.3em; + border-bottom: 1px solid #1F1F1F; + border-top: 1px solid #222; + background-color: #333; + + font-size: 1em; line-height: 1.0em; font-weight: normal; font-family: Helvetica, Arial, sans-serif; + + background: #333; + background: -moz-linear-gradient(top, #222, #333); + background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#222), to(#333)); + background: -webkit-linear-gradient(top, #222, #333); + background: -o-linear-gradient(top, #333, #222); + background: -ms-linear-gradient(top, #333, #222); + background: linear-gradient(top, #333, #222); + + /* 1px top shadow */ +/* -webkit-box-shadow: 0px -1px 0px 0px rgba(0, 0, 0, 0.15); -moz-box-shadow: 0px -1px 0px 0px rgba(0, 0, 0, 0.15); box-shadow: 0px -1px 0px 0px rgba(0, 0, 0, 0.15);*/ +} + +.vjs-default-skin .vjs-current-time { left: 0; } + +.vjs-default-skin .vjs-duration { right: 0; display: none; } +.vjs-default-skin .vjs-remaining-time { right: 0; } + +.vjs-time-divider { display:none; } + +.vjs-default-skin .vjs-time-control { font-size: 1em; line-height: 1; font-weight: normal; font-family: Helvetica, Arial, sans-serif; } +.vjs-default-skin .vjs-time-control span { line-height: 25px; /* Centering vertically */ } + +/* Fullscreen +-------------------------------------------------------------------------------- */ +.vjs-secondary-controls { float: right; } + +.vjs-default-skin .vjs-fullscreen-control { width: 3.8em; cursor: pointer !important; float: right; } +.vjs-default-skin .vjs-fullscreen-control div { width: 16px; height: 16px; background: url('video-js.png') -50px 0; margin: 0.5em auto 0; } + +.vjs-default-skin.vjs-fullscreen .vjs-fullscreen-control div { background: url('video-js.png') -75px 0; } + + +/* Big Play Button (at start) +---------------------------------------------------------*/ +.vjs-default-skin .vjs-big-play-button { + display: block; /* Start hidden */ z-index: 2; + position: absolute; top: 50%; left: 50%; width: 8.0em; height: 8.0em; margin: -43px 0 0 -43px; text-align: center; vertical-align: center; cursor: pointer !important; + border: 0.3em solid #fff; opacity: 0.95; + -webkit-border-radius: 25px; -moz-border-radius: 25px; border-radius: 25px; + + background: #454545; + background: -moz-linear-gradient(top, #454545 0%, #232323 50%, #161616 50%, #3f3f3f 100%); + background: -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(0%,#454545), color-stop(50%,#232323), color-stop(50%,#161616), color-stop(100%,#3f3f3f)); + background: -webkit-linear-gradient(top, #454545 0%,#232323 50%,#161616 50%,#3f3f3f 100%); + background: -o-linear-gradient(top, #454545 0%,#232323 50%,#161616 50%,#3f3f3f 100%); + background: -ms-linear-gradient(top, #454545 0%,#232323 50%,#161616 50%,#3f3f3f 100%); + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#454545', endColorstr='#3f3f3f',GradientType=0 ); + background: linear-gradient(top, #454545 0%,#232323 50%,#161616 50%,#3f3f3f 100%); + + /* CSS Shadows */ + -webkit-box-shadow: 4px 4px 8px #000; -moz-box-shadow: 4px 4px 8px #000; box-shadow: 4px 4px 8px #000; +} + +.vjs-default-skin div.vjs-big-play-button:hover { + -webkit-box-shadow: 0 0 80px #fff; -moz-box-shadow: 0 0 80px #fff; box-shadow: 0 0 80px #fff; +} + +.vjs-default-skin div.vjs-big-play-button span { + position: absolute; top: 50%; left: 50%; + display: block; width: 35px; height: 42px; + margin: -20px 0 0 -15px; /* Using negative margin to center image. */ + background: url('video-js.png') -100px 0; +} + +/* Loading Spinner +---------------------------------------------------------*/ +/* CSS Spinners by Kilian Valkhof - http://kilianvalkhof.com/2010/css-xhtml/css3-loading-spinners-without-images/ */ +.vjs-loading-spinner { + display: none; + position: absolute; top: 50%; left: 50%; width: 55px; height: 55px; + margin: -28px 0 0 -28px; + -webkit-animation-name: rotatethis; + -webkit-animation-duration:1s; + -webkit-animation-iteration-count:infinite; + -webkit-animation-timing-function:linear; + -moz-animation-name: rotatethis; + -moz-animation-duration:1s; + -moz-animation-iteration-count:infinite; + -moz-animation-timing-function:linear; +} + +@-webkit-keyframes rotatethis { + 0% {-webkit-transform:scale(0.6) rotate(0deg); } + 12.5% {-webkit-transform:scale(0.6) rotate(0deg); } + 12.51% {-webkit-transform:scale(0.6) rotate(45deg); } + 25% {-webkit-transform:scale(0.6) rotate(45deg); } + 25.01% {-webkit-transform:scale(0.6) rotate(90deg);} + 37.5% {-webkit-transform:scale(0.6) rotate(90deg);} + 37.51% {-webkit-transform:scale(0.6) rotate(135deg);} + 50% {-webkit-transform:scale(0.6) rotate(135deg);} + 50.01% {-webkit-transform:scale(0.6) rotate(180deg);} + 62.5% {-webkit-transform:scale(0.6) rotate(180deg);} + 62.51% {-webkit-transform:scale(0.6) rotate(225deg);} + 75% {-webkit-transform:scale(0.6) rotate(225deg);} + 75.01% {-webkit-transform:scale(0.6) rotate(270deg);} + 87.5% {-webkit-transform:scale(0.6) rotate(270deg);} + 87.51% {-webkit-transform:scale(0.6) rotate(315deg);} + 100% {-webkit-transform:scale(0.6) rotate(315deg);} +} + +@-moz-keyframes rotatethis { + 0% {-moz-transform:scale(0.6) rotate(0deg);} + 12.5% {-moz-transform:scale(0.6) rotate(0deg);} + 12.51% {-moz-transform:scale(0.6) rotate(45deg);} + 25% {-moz-transform:scale(0.6) rotate(45deg);} + 25.01% {-moz-transform:scale(0.6) rotate(90deg);} + 37.5% {-moz-transform:scale(0.6) rotate(90deg);} + 37.51% {-moz-transform:scale(0.6) rotate(135deg);} + 50% {-moz-transform:scale(0.6) rotate(135deg);} + 50.01% {-moz-transform:scale(0.6) rotate(180deg);} + 62.5% {-moz-transform:scale(0.6) rotate(180deg);} + 62.51% {-moz-transform:scale(0.6) rotate(225deg);} + 75% {-moz-transform:scale(0.6) rotate(225deg);} + 75.01% {-moz-transform:scale(0.6) rotate(270deg);} + 87.5% {-moz-transform:scale(0.6) rotate(270deg);} + 87.51% {-moz-transform:scale(0.6) rotate(315deg);} + 100% {-moz-transform:scale(0.6) rotate(315deg);} +} +/* Each circle */ +div.vjs-loading-spinner .ball1 { opacity: 0.12; position:absolute; left: 20px; top: 0px; width: 13px; height: 13px; background: #fff; + border-radius: 13px; -webkit-border-radius: 13px; -moz-border-radius: 13px; border: 1px solid #ccc; } + +div.vjs-loading-spinner .ball2 { opacity: 0.25; position:absolute; left: 34px; top: 6px; width: 13px; height: 13px; background: #fff; + border-radius: 13px; -webkit-border-radius: 13px; -moz-border-radius: 13px; border: 1px solid #ccc; } + +div.vjs-loading-spinner .ball3 { opacity: 0.37; position:absolute; left: 40px; top: 20px; width: 13px; height: 13px; background: #fff; + border-radius: 13px; -webkit-border-radius: 13px; -moz-border-radius: 13px; border: 1px solid #ccc; } + +div.vjs-loading-spinner .ball4 { opacity: 0.50; position:absolute; left: 34px; top: 34px; width: 13px; height: 13px; background: #fff; + border-radius: 10px; -webkit-border-radius: 10px; -moz-border-radius: 15px; border: 1px solid #ccc; } + +div.vjs-loading-spinner .ball5 { opacity: 0.62; position:absolute; left: 20px; top: 40px; width: 13px; height: 13px; background: #fff; + border-radius: 13px; -webkit-border-radius: 13px; -moz-border-radius: 13px; border: 1px solid #ccc; } + +div.vjs-loading-spinner .ball6 { opacity: 0.75; position:absolute; left: 6px; top: 34px; width: 13px; height: 13px; background: #fff; + border-radius: 13px; -webkit-border-radius: 13px; -moz-border-radius: 13px; border: 1px solid #ccc; } + +div.vjs-loading-spinner .ball7 { opacity: 0.87; position:absolute; left: 0px; top: 20px; width: 13px; height: 13px; background: #fff; + border-radius: 13px; -webkit-border-radius: 13px; -moz-border-radius: 13px; border: 1px solid #ccc; } + +div.vjs-loading-spinner .ball8 { opacity: 1.00; position:absolute; left: 6px; top: 6px; width: 13px; height: 13px; background: #fff; + border-radius: 13px; -webkit-border-radius: 13px; -moz-border-radius: 13px; border: 1px solid #ccc; } diff --git a/extlib/video-js/video-js.min.css b/extlib/video-js/video-js.min.css new file mode 100644 index 00000000..4394cdd7 --- /dev/null +++ b/extlib/video-js/video-js.min.css @@ -0,0 +1 @@ +.video-js{background-color:#000;position:relative;padding:0;font-size:10px;vertical-align:middle}.video-js .vjs-tech{position:absolute;top:0;left:0;width:100%;height:100%}.video-js:-moz-full-screen{position:absolute}body.vjs-full-window{padding:0;margin:0;height:100%;overflow-y:auto}.video-js.vjs-fullscreen{position:fixed;overflow:hidden;z-index:1000;left:0;top:0;bottom:0;right:0;width:100%!important;height:100%!important;_position:absolute}.video-js:-webkit-full-screen{width:100%!important;height:100%!important}.vjs-poster{margin:0 auto;padding:0;cursor:pointer;position:relative;width:100%;max-height:100%}.video-js .vjs-subtitles{color:#fff;font-size:20px;text-align:center;position:absolute;bottom:40px;left:0;right:0}.vjs-fade-in{visibility:visible!important;opacity:1!important;-webkit-transition:visibility 0s linear 0s,opacity .3s linear;-moz-transition:visibility 0s linear 0s,opacity .3s linear;-ms-transition:visibility 0s linear 0s,opacity .3s linear;-o-transition:visibility 0s linear 0s,opacity .3s linear;transition:visibility 0s linear 0s,opacity .3s linear}.vjs-fade-out{visibility:hidden!important;opacity:0!important;-webkit-transition:visibility 0s linear 1.5s,opacity 1.5s linear;-moz-transition:visibility 0s linear 1.5s,opacity 1.5s linear;-ms-transition:visibility 0s linear 1.5s,opacity 1.5s linear;-o-transition:visibility 0s linear 1.5s,opacity 1.5s linear;transition:visibility 0s linear 1.5s,opacity 1.5s linear}.vjs-default-skin .vjs-controls{position:absolute;bottom:0;left:0;right:0;margin:0;padding:0;height:2.6em;color:#fff;border-top:1px solid #404040;background:#242424;background:-moz-linear-gradient(top,#242424 50%,#1f1f1f 50%,#171717 100%);background:-webkit-gradient(linear,0% 0,0% 100%,color-stop(50%,#242424),color-stop(50%,#1f1f1f),color-stop(100%,#171717));background:-webkit-linear-gradient(top,#242424 50%,#1f1f1f 50%,#171717 100%);background:-o-linear-gradient(top,#242424 50%,#1f1f1f 50%,#171717 100%);background:-ms-linear-gradient(top,#242424 50%,#1f1f1f 50%,#171717 100%);background:linear-gradient(top,#242424 50%,#1f1f1f 50%,#171717 100%);visibility:hidden;opacity:0}.vjs-default-skin .vjs-control{position:relative;float:left;text-align:center;margin:0;padding:0;height:2.6em;width:2.6em}.vjs-default-skin .vjs-control:focus{outline:0}.vjs-default-skin .vjs-control-text{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.vjs-default-skin .vjs-play-control{width:5em;cursor:pointer!important}.vjs-default-skin.vjs-paused .vjs-play-control div{width:15px;height:17px;background:url('video-js.png');margin:.5em auto 0}.vjs-default-skin.vjs-playing .vjs-play-control div{width:15px;height:17px;background:url('video-js.png') -25px 0;margin:.5em auto 0}.vjs-default-skin .vjs-rewind-control{width:5em;cursor:pointer!important}.vjs-default-skin .vjs-rewind-control div{width:19px;height:16px;background:url('video-js.png');margin:.5em auto 0}.vjs-default-skin .vjs-mute-control{width:3.8em;cursor:pointer!important;float:right}.vjs-default-skin .vjs-mute-control div{width:22px;height:16px;background:url('video-js.png') -75px -25px;margin:.5em auto 0}.vjs-default-skin .vjs-mute-control.vjs-vol-0 div{background:url('video-js.png') 0 -25px}.vjs-default-skin .vjs-mute-control.vjs-vol-1 div{background:url('video-js.png') -25px -25px}.vjs-default-skin .vjs-mute-control.vjs-vol-2 div{background:url('video-js.png') -50px -25px}.vjs-default-skin .vjs-volume-control{width:5em;float:right}.vjs-default-skin .vjs-volume-bar{position:relative;width:5em;height:.6em;margin:1em auto 0;cursor:pointer!important;-moz-border-radius:.3em;-webkit-border-radius:.3em;border-radius:.3em;background:#666;background:-moz-linear-gradient(top,#333,#666);background:-webkit-gradient(linear,0% 0,0% 100%,from(#333),to(#666));background:-webkit-linear-gradient(top,#333,#666);background:-o-linear-gradient(top,#333,#666);background:-ms-linear-gradient(top,#333,#666);background:linear-gradient(top,#333,#666)}.vjs-default-skin .vjs-volume-level{position:absolute;top:0;left:0;height:.6em;-moz-border-radius:.3em;-webkit-border-radius:.3em;border-radius:.3em;background:#fff;background:-moz-linear-gradient(top,#fff,#ccc);background:-webkit-gradient(linear,0% 0,0% 100%,from(#fff),to(#ccc));background:-webkit-linear-gradient(top,#fff,#ccc);background:-o-linear-gradient(top,#fff,#ccc);background:-ms-linear-gradient(top,#fff,#ccc);background:linear-gradient(top,#fff,#ccc)}.vjs-default-skin .vjs-volume-handle{position:absolute;top:-0.2em;width:.8em;height:.8em;background:#ccc;left:0;border:1px solid #fff;-moz-border-radius:.6em;-webkit-border-radius:.6em;border-radius:.6em}.vjs-default-skin div.vjs-progress-control{position:absolute;left:4.8em;right:4.8em;height:1.0em;width:auto;top:-1.3em;border-bottom:1px solid #1f1f1f;border-top:1px solid #222;background:#333;background:-moz-linear-gradient(top,#222,#333);background:-webkit-gradient(linear,0% 0,0% 100%,from(#222),to(#333));background:-webkit-linear-gradient(top,#222,#333);background:-o-linear-gradient(top,#333,#222);background:-ms-linear-gradient(top,#333,#222);background:linear-gradient(top,#333,#222)}.vjs-default-skin .vjs-progress-holder{position:relative;cursor:pointer!important;padding:0;margin:0;height:1.0em;-moz-border-radius:.6em;-webkit-border-radius:.6em;border-radius:.6em;background:#111;background:-moz-linear-gradient(top,#111,#262626);background:-webkit-gradient(linear,0% 0,0% 100%,from(#111),to(#262626));background:-webkit-linear-gradient(top,#111,#262626);background:-o-linear-gradient(top,#111,#262626);background:-ms-linear-gradient(top,#111,#262626);background:linear-gradient(top,#111,#262626)}.vjs-default-skin .vjs-progress-holder .vjs-play-progress,.vjs-default-skin .vjs-progress-holder .vjs-load-progress{position:absolute;display:block;height:1.0em;margin:0;padding:0;left:0;top:0;-moz-border-radius:.6em;-webkit-border-radius:.6em;border-radius:.6em}.vjs-default-skin .vjs-play-progress{background:#fff;background:-moz-linear-gradient(top,#fff 0,#d6d6d6 50%,#fff 100%);background:-webkit-gradient(linear,0% 0,0% 100%,color-stop(0%,#fff),color-stop(50%,#d6d6d6),color-stop(100%,#fff));background:-webkit-linear-gradient(top,#fff 0,#d6d6d6 50%,#fff 100%);background:-o-linear-gradient(top,#fff 0,#d6d6d6 50%,#fff 100%);background:-ms-linear-gradient(top,#fff 0,#d6d6d6 50%,#fff 100%);background:linear-gradient(top,#fff 0,#d6d6d6 50%,#fff 100%);background:#efefef;background:-moz-linear-gradient(top,#efefef 0,#f5f5f5 50%,#dbdbdb 50%,#f1f1f1 100%);background:-webkit-gradient(linear,0% 0,0% 100%,color-stop(0%,#efefef),color-stop(50%,#f5f5f5),color-stop(50%,#dbdbdb),color-stop(100%,#f1f1f1));background:-webkit-linear-gradient(top,#efefef 0,#f5f5f5 50%,#dbdbdb 50%,#f1f1f1 100%);background:-o-linear-gradient(top,#efefef 0,#f5f5f5 50%,#dbdbdb 50%,#f1f1f1 100%);background:-ms-linear-gradient(top,#efefef 0,#f5f5f5 50%,#dbdbdb 50%,#f1f1f1 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#efefef',endColorstr='#f1f1f1',GradientType=0);background:linear-gradient(top,#efefef 0,#f5f5f5 50%,#dbdbdb 50%,#f1f1f1 100%)}.vjs-default-skin .vjs-load-progress{opacity:.8;background:#666;background:-moz-linear-gradient(top,#666,#333);background:-webkit-gradient(linear,0% 0,0% 100%,from(#666),to(#333));background:-webkit-linear-gradient(top,#666,#333);background:-o-linear-gradient(top,#666,#333);background:-ms-linear-gradient(top,#666,#333);background:linear-gradient(top,#666,#333)}.vjs-default-skin div.vjs-seek-handle{position:absolute;width:16px;height:16px;margin-top:-0.3em;left:0;top:0;background:url('video-js.png') 0 -50px;-moz-border-radius:.8em;-webkit-border-radius:.8em;border-radius:.8em;-webkit-box-shadow:0 2px 4px 0 #000;-moz-box-shadow:0 2px 4px 0 #000;box-shadow:0 2px 4px 0 #000}.vjs-default-skin .vjs-time-controls{position:absolute;right:0;height:1.0em;width:4.8em;top:-1.3em;border-bottom:1px solid #1f1f1f;border-top:1px solid #222;background-color:#333;font-size:1em;line-height:1.0em;font-weight:normal;font-family:Helvetica,Arial,sans-serif;background:#333;background:-moz-linear-gradient(top,#222,#333);background:-webkit-gradient(linear,0% 0,0% 100%,from(#222),to(#333));background:-webkit-linear-gradient(top,#222,#333);background:-o-linear-gradient(top,#333,#222);background:-ms-linear-gradient(top,#333,#222);background:linear-gradient(top,#333,#222)}.vjs-default-skin .vjs-current-time{left:0}.vjs-default-skin .vjs-duration{right:0;display:none}.vjs-default-skin .vjs-remaining-time{right:0}.vjs-time-divider{display:none}.vjs-default-skin .vjs-time-control{font-size:1em;line-height:1;font-weight:normal;font-family:Helvetica,Arial,sans-serif}.vjs-default-skin .vjs-time-control span{line-height:25px}.vjs-secondary-controls{float:right}.vjs-default-skin .vjs-fullscreen-control{width:3.8em;cursor:pointer!important;float:right}.vjs-default-skin .vjs-fullscreen-control div{width:16px;height:16px;background:url('video-js.png') -50px 0;margin:.5em auto 0}.vjs-default-skin.vjs-fullscreen .vjs-fullscreen-control div{background:url('video-js.png') -75px 0}.vjs-default-skin .vjs-big-play-button{display:block;z-index:2;position:absolute;top:50%;left:50%;width:8.0em;height:8.0em;margin:-43px 0 0 -43px;text-align:center;vertical-align:center;cursor:pointer!important;border:.3em solid #fff;opacity:.95;-webkit-border-radius:25px;-moz-border-radius:25px;border-radius:25px;background:#454545;background:-moz-linear-gradient(top,#454545 0,#232323 50%,#161616 50%,#3f3f3f 100%);background:-webkit-gradient(linear,0% 0,0% 100%,color-stop(0%,#454545),color-stop(50%,#232323),color-stop(50%,#161616),color-stop(100%,#3f3f3f));background:-webkit-linear-gradient(top,#454545 0,#232323 50%,#161616 50%,#3f3f3f 100%);background:-o-linear-gradient(top,#454545 0,#232323 50%,#161616 50%,#3f3f3f 100%);background:-ms-linear-gradient(top,#454545 0,#232323 50%,#161616 50%,#3f3f3f 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#454545',endColorstr='#3f3f3f',GradientType=0);background:linear-gradient(top,#454545 0,#232323 50%,#161616 50%,#3f3f3f 100%);-webkit-box-shadow:4px 4px 8px #000;-moz-box-shadow:4px 4px 8px #000;box-shadow:4px 4px 8px #000}.vjs-default-skin div.vjs-big-play-button:hover{-webkit-box-shadow:0 0 80px #fff;-moz-box-shadow:0 0 80px #fff;box-shadow:0 0 80px #fff}.vjs-default-skin div.vjs-big-play-button span{position:absolute;top:50%;left:50%;display:block;width:35px;height:42px;margin:-20px 0 0 -15px;background:url('video-js.png') -100px 0}.vjs-loading-spinner{display:none;position:absolute;top:50%;left:50%;width:55px;height:55px;margin:-28px 0 0 -28px;-webkit-animation-name:rotatethis;-webkit-animation-duration:1s;-webkit-animation-iteration-count:infinite;-webkit-animation-timing-function:linear;-moz-animation-name:rotatethis;-moz-animation-duration:1s;-moz-animation-iteration-count:infinite;-moz-animation-timing-function:linear}@-webkit-keyframes rotatethis{0%{-webkit-transform:scale(0.6) rotate(0deg)}12.5%{-webkit-transform:scale(0.6) rotate(0deg)}12.51%{-webkit-transform:scale(0.6) rotate(45deg)}25%{-webkit-transform:scale(0.6) rotate(45deg)}25.01%{-webkit-transform:scale(0.6) rotate(90deg)}37.5%{-webkit-transform:scale(0.6) rotate(90deg)}37.51%{-webkit-transform:scale(0.6) rotate(135deg)}50%{-webkit-transform:scale(0.6) rotate(135deg)}50.01%{-webkit-transform:scale(0.6) rotate(180deg)}62.5%{-webkit-transform:scale(0.6) rotate(180deg)}62.51%{-webkit-transform:scale(0.6) rotate(225deg)}75%{-webkit-transform:scale(0.6) rotate(225deg)}75.01%{-webkit-transform:scale(0.6) rotate(270deg)}87.5%{-webkit-transform:scale(0.6) rotate(270deg)}87.51%{-webkit-transform:scale(0.6) rotate(315deg)}100%{-webkit-transform:scale(0.6) rotate(315deg)}}@-moz-keyframes rotatethis{0%{-moz-transform:scale(0.6) rotate(0deg)}12.5%{-moz-transform:scale(0.6) rotate(0deg)}12.51%{-moz-transform:scale(0.6) rotate(45deg)}25%{-moz-transform:scale(0.6) rotate(45deg)}25.01%{-moz-transform:scale(0.6) rotate(90deg)}37.5%{-moz-transform:scale(0.6) rotate(90deg)}37.51%{-moz-transform:scale(0.6) rotate(135deg)}50%{-moz-transform:scale(0.6) rotate(135deg)}50.01%{-moz-transform:scale(0.6) rotate(180deg)}62.5%{-moz-transform:scale(0.6) rotate(180deg)}62.51%{-moz-transform:scale(0.6) rotate(225deg)}75%{-moz-transform:scale(0.6) rotate(225deg)}75.01%{-moz-transform:scale(0.6) rotate(270deg)}87.5%{-moz-transform:scale(0.6) rotate(270deg)}87.51%{-moz-transform:scale(0.6) rotate(315deg)}100%{-moz-transform:scale(0.6) rotate(315deg)}}div.vjs-loading-spinner .ball1{opacity:.12;position:absolute;left:20px;top:0;width:13px;height:13px;background:#fff;border-radius:13px;-webkit-border-radius:13px;-moz-border-radius:13px;border:1px solid #ccc}div.vjs-loading-spinner .ball2{opacity:.25;position:absolute;left:34px;top:6px;width:13px;height:13px;background:#fff;border-radius:13px;-webkit-border-radius:13px;-moz-border-radius:13px;border:1px solid #ccc}div.vjs-loading-spinner .ball3{opacity:.37;position:absolute;left:40px;top:20px;width:13px;height:13px;background:#fff;border-radius:13px;-webkit-border-radius:13px;-moz-border-radius:13px;border:1px solid #ccc}div.vjs-loading-spinner .ball4{opacity:.50;position:absolute;left:34px;top:34px;width:13px;height:13px;background:#fff;border-radius:10px;-webkit-border-radius:10px;-moz-border-radius:15px;border:1px solid #ccc}div.vjs-loading-spinner .ball5{opacity:.62;position:absolute;left:20px;top:40px;width:13px;height:13px;background:#fff;border-radius:13px;-webkit-border-radius:13px;-moz-border-radius:13px;border:1px solid #ccc}div.vjs-loading-spinner .ball6{opacity:.75;position:absolute;left:6px;top:34px;width:13px;height:13px;background:#fff;border-radius:13px;-webkit-border-radius:13px;-moz-border-radius:13px;border:1px solid #ccc}div.vjs-loading-spinner .ball7{opacity:.87;position:absolute;left:0;top:20px;width:13px;height:13px;background:#fff;border-radius:13px;-webkit-border-radius:13px;-moz-border-radius:13px;border:1px solid #ccc}div.vjs-loading-spinner .ball8{opacity:1.00;position:absolute;left:6px;top:6px;width:13px;height:13px;background:#fff;border-radius:13px;-webkit-border-radius:13px;-moz-border-radius:13px;border:1px solid #ccc}
\ No newline at end of file diff --git a/extlib/video-js/video-js.png b/extlib/video-js/video-js.png Binary files differnew file mode 100644 index 00000000..c692d123 --- /dev/null +++ b/extlib/video-js/video-js.png diff --git a/extlib/video-js/video-js.swf b/extlib/video-js/video-js.swf Binary files differnew file mode 100644 index 00000000..3273af5a --- /dev/null +++ b/extlib/video-js/video-js.swf diff --git a/extlib/video-js/video.js b/extlib/video-js/video.js new file mode 100644 index 00000000..c41cceec --- /dev/null +++ b/extlib/video-js/video.js @@ -0,0 +1,3744 @@ +/*! +Video.js - HTML5 Video Player +Version 3.1.0 + +LGPL v3 LICENSE INFO +This file is part of Video.js. Copyright 2011 Zencoder, Inc. + +Video.js is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Video.js is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with Video.js. If not, see <http://www.gnu.org/licenses/>. +*/ + +// Self-executing function to prevent global vars and help with minification +;(function(window, undefined){ + var document = window.document;// HTML5 Shiv. Must be in <head> to support older browsers. +document.createElement("video");document.createElement("audio"); + +var VideoJS = function(id, addOptions, ready){ + var tag; // Element of ID + + // Allow for element or ID to be passed in + // String ID + if (typeof id == "string") { + + // Adjust for jQuery ID syntax + if (id.indexOf("#") === 0) { + id = id.slice(1); + } + + // If a player instance has already been created for this ID return it. + if (_V_.players[id]) { + return _V_.players[id]; + + // Otherwise get element for ID + } else { + tag = _V_.el(id) + } + + // ID is a media element + } else { + tag = id; + } + + // Check for a useable element + if (!tag || !tag.nodeName) { // re: nodeName, could be a box div also + throw new TypeError("The element or ID supplied is not valid. (VideoJS)"); // Returns + } + + // Element may have a player attr referring to an already created player instance. + // If not, set up a new player and return the instance. + return tag.player || new _V_.Player(tag, addOptions, ready); +}, + +// Shortcut +_V_ = VideoJS, + +// CDN Version. Used to target right flash swf. +CDN_VERSION = "3.1"; + +VideoJS.players = {}; + +VideoJS.options = { + + // Default order of fallback technology + techOrder: ["html5","flash"], + // techOrder: ["flash","html5"], + + html5: {}, + flash: { swf: "http://vjs.zencdn.net/c/video-js.swf" }, + + // Default of web browser is 300x150. Should rely on source width/height. + width: "auto", + height: "auto", + + // defaultVolume: 0.85, + defaultVolume: 0.00, // The freakin seaguls are driving me crazy! + + // Included control sets + components: { + "poster": {}, + "loadingSpinner": {}, + "bigPlayButton": {}, + "controlBar": {}, + "subtitlesDisplay": {} + } + + // components: [ + // "poster", + // "loadingSpinner", + // "bigPlayButton", + // { name: "controlBar", options: { + // components: [ + // "playToggle", + // "fullscreenToggle", + // "currentTimeDisplay", + // "timeDivider", + // "durationDisplay", + // "remainingTimeDisplay", + // { name: "progressControl", options: { + // components: [ + // { name: "seekBar", options: { + // components: [ + // "loadProgressBar", + // "playProgressBar", + // "seekHandle" + // ]} + // } + // ]} + // }, + // { name: "volumeControl", options: { + // components: [ + // { name: "volumeBar", options: { + // components: [ + // "volumeLevel", + // "volumeHandle" + // ]} + // } + // ]} + // }, + // "muteToggle" + // ] + // }}, + // "subtitlesDisplay"/*, "replay"*/ + // ] +}; + +// Set CDN Version of swf +if (CDN_VERSION != "GENERATED_CDN_VSN") { + _V_.options.flash.swf = "http://vjs.zencdn.net/"+CDN_VERSION+"/video-js.swf" +} + +// Automatically set up any tags that have a data-setup attribute +_V_.autoSetup = function(){ + var options, vid, player, + vids = document.getElementsByTagName("video"); + + // Check if any media elements exist + if (vids && vids.length > 0) { + + for (var i=0,j=vids.length; i<j; i++) { + vid = vids[i]; + + // Check if element exists, has getAttribute func. + // IE seems to consider typeof el.getAttribute == "object" instead of "function" like expected, at least when loading the player immediately. + if (vid && vid.getAttribute) { + + // Make sure this player hasn't already been set up. + if (vid.player === undefined) { + options = vid.getAttribute("data-setup"); + + // Check if data-setup attr exists. + // We only auto-setup if they've added the data-setup attr. + if (options !== null) { + + // Parse options JSON + // If empty string, make it a parsable json object. + options = JSON.parse(options || "{}"); + + // Create new video.js instance. + player = _V_(vid, options); + } + } + + // If getAttribute isn't defined, we need to wait for the DOM. + } else { + _V_.autoSetupTimeout(1); + break; + } + } + + // No videos were found, so keep looping unless page is finisehd loading. + } else if (!_V_.windowLoaded) { + _V_.autoSetupTimeout(1); + } +}; + +// Pause to let the DOM keep processing +_V_.autoSetupTimeout = function(wait){ + setTimeout(_V_.autoSetup, wait); +}; +_V_.merge = function(obj1, obj2, safe){ + // Make sure second object exists + if (!obj2) { obj2 = {}; }; + + for (var attrname in obj2){ + if (obj2.hasOwnProperty(attrname) && (!safe || !obj1.hasOwnProperty(attrname))) { obj1[attrname]=obj2[attrname]; } + } + return obj1; +}; +_V_.extend = function(obj){ this.merge(this, obj, true); }; + +_V_.extend({ + tech: {}, // Holder for playback technology settings + controlSets: {}, // Holder for control set definitions + + // Device Checks + isIE: function(){ return !+"\v1"; }, + isFF: function(){ return !!_V_.ua.match("Firefox") }, + isIPad: function(){ return navigator.userAgent.match(/iPad/i) !== null; }, + isIPhone: function(){ return navigator.userAgent.match(/iPhone/i) !== null; }, + isIOS: function(){ return VideoJS.isIPhone() || VideoJS.isIPad(); }, + iOSVersion: function() { + var match = navigator.userAgent.match(/OS (\d+)_/i); + if (match && match[1]) { return match[1]; } + }, + isAndroid: function(){ return navigator.userAgent.match(/Android.*AppleWebKit/i) !== null; }, + androidVersion: function() { + var match = navigator.userAgent.match(/Android (\d+)\./i); + if (match && match[1]) { return match[1]; } + }, + + testVid: document.createElement("video"), + ua: navigator.userAgent, + support: {}, + + each: function(arr, fn){ + if (!arr || arr.length === 0) { return; } + for (var i=0,j=arr.length; i<j; i++) { + fn.call(this, arr[i], i); + } + }, + + eachProp: function(obj, fn){ + if (!obj) { return; } + for (var name in obj) { + if (obj.hasOwnProperty(name)) { + fn.call(this, name, obj[name]); + } + } + }, + + el: function(id){ return document.getElementById(id); }, + createElement: function(tagName, attributes){ + var el = document.createElement(tagName), + attrname; + for (attrname in attributes){ + if (attributes.hasOwnProperty(attrname)) { + if (attrname.indexOf("-") !== -1) { + el.setAttribute(attrname, attributes[attrname]); + } else { + el[attrname] = attributes[attrname]; + } + } + } + return el; + }, + + insertFirst: function(node, parent){ + if (parent.firstChild) { + parent.insertBefore(node, parent.firstChild); + } else { + parent.appendChild(node); + } + }, + + addClass: function(element, classToAdd){ + if ((" "+element.className+" ").indexOf(" "+classToAdd+" ") == -1) { + element.className = element.className === "" ? classToAdd : element.className + " " + classToAdd; + } + }, + + removeClass: function(element, classToRemove){ + if (element.className.indexOf(classToRemove) == -1) { return; } + var classNames = element.className.split(" "); + classNames.splice(classNames.indexOf(classToRemove),1); + element.className = classNames.join(" "); + }, + + remove: function(item, array){ + if (!array) return; + var i = array.indexOf(item); + if (i != -1) { + return array.splice(i, 1) + }; + }, + + // Attempt to block the ability to select text while dragging controls + blockTextSelection: function(){ + document.body.focus(); + document.onselectstart = function () { return false; }; + }, + // Turn off text selection blocking + unblockTextSelection: function(){ document.onselectstart = function () { return true; }; }, + + // Return seconds as H:MM:SS or M:SS + // Supplying a guide (in seconds) will include enough leading zeros to cover the length of the guide + formatTime: function(seconds, guide) { + guide = guide || seconds; // Default to using seconds as guide + var s = Math.floor(seconds % 60), + m = Math.floor(seconds / 60 % 60), + h = Math.floor(seconds / 3600), + gm = Math.floor(guide / 60 % 60), + gh = Math.floor(guide / 3600); + + // Check if we need to show hours + h = (h > 0 || gh > 0) ? h + ":" : ""; + + // If hours are showing, we may need to add a leading zero. + // Always show at least one digit of minutes. + m = (((h || gm >= 10) && m < 10) ? "0" + m : m) + ":"; + + // Check if leading zero is need for seconds + s = (s < 10) ? "0" + s : s; + + return h + m + s; + }, + + capitalize: function(string){ + return string.charAt(0).toUpperCase() + string.slice(1); + }, + + // Return the relative horizonal position of an event as a value from 0-1 + getRelativePosition: function(x, relativeElement){ + return Math.max(0, Math.min(1, (x - _V_.findPosX(relativeElement)) / relativeElement.offsetWidth)); + }, + + getComputedStyleValue: function(element, style){ + return window.getComputedStyle(element, null).getPropertyValue(style); + }, + + trim: function(string){ return string.toString().replace(/^\s+/, "").replace(/\s+$/, ""); }, + round: function(num, dec) { + if (!dec) { dec = 0; } + return Math.round(num*Math.pow(10,dec))/Math.pow(10,dec); + }, + + isEmpty: function(object) { + for (var prop in object) { + return false; + } + return true; + }, + + // Mimic HTML5 TimeRange Spec. + createTimeRange: function(start, end){ + return { + length: 1, + start: function() { return start; }, + end: function() { return end; } + }; + }, + + /* Element Data Store. Allows for binding data to an element without putting it directly on the element. + Ex. Event listneres are stored here. + (also from jsninja.com) + ================================================================================ */ + cache: {}, // Where the data is stored + guid: 1, // Unique ID for the element + expando: "vdata" + (new Date).getTime(), // Unique attribute to store element's guid in + + // Returns the cache object where data for the element is stored + getData: function(elem){ + var id = elem[_V_.expando]; + if (!id) { + id = elem[_V_.expando] = _V_.guid++; + _V_.cache[id] = {}; + } + return _V_.cache[id]; + }, + // Delete data for the element from the cache and the guid attr from element + removeData: function(elem){ + var id = elem[_V_.expando]; + if (!id) { return; } + // Remove all stored data + delete _V_.cache[id]; + // Remove the expando property from the DOM node + try { + delete elem[_V_.expando]; + } catch(e) { + if (elem.removeAttribute) { + elem.removeAttribute(_V_.expando); + } else { + // IE doesn't appear to support removeAttribute on the document element + elem[_V_.expando] = null; + } + } + }, + + /* Proxy (a.k.a Bind or Context). A simple method for changing the context of a function + It also stores a unique id on the function so it can be easily removed from events + ================================================================================ */ + proxy: function(context, fn) { + // Make sure the function has a unique ID + if (!fn.guid) { fn.guid = _V_.guid++; } + // Create the new function that changes the context + var ret = function() { + return fn.apply(context, arguments); + }; + + // Give the new function the same ID + // (so that they are equivalent and can be easily removed) + ret.guid = fn.guid; + + return ret; + }, + + get: function(url, onSuccess, onError){ + // if (netscape.security.PrivilegeManager.enablePrivilege) { + // netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead"); + // } + + var local = (url.indexOf("file:") == 0 || (window.location.href.indexOf("file:") == 0 && url.indexOf("http:") == -1)); + + if (typeof XMLHttpRequest == "undefined") { + XMLHttpRequest = function () { + try { return new ActiveXObject("Msxml2.XMLHTTP.6.0"); } catch (e) {} + try { return new ActiveXObject("Msxml2.XMLHTTP.3.0"); } catch (f) {} + try { return new ActiveXObject("Msxml2.XMLHTTP"); } catch (g) {} + throw new Error("This browser does not support XMLHttpRequest."); + }; + } + + var request = new XMLHttpRequest(); + + try { + request.open("GET", url); + } catch(e) { + _V_.log("VideoJS XMLHttpRequest (open)", e); + // onError(e); + return false; + } + + request.onreadystatechange = _V_.proxy(this, function() { + if (request.readyState == 4) { + if (request.status == 200 || local && request.status == 0) { + onSuccess(request.responseText); + } else { + if (onError) { + onError(); + } + } + } + }); + + try { + request.send(); + } catch(e) { + _V_.log("VideoJS XMLHttpRequest (send)", e); + if (onError) { + onError(e); + } + } + }, + + /* Local Storage + ================================================================================ */ + setLocalStorage: function(key, value){ + // IE was throwing errors referencing the var anywhere without this + var localStorage = localStorage || false; + if (!localStorage) { return; } + try { + localStorage[key] = value; + } catch(e) { + if (e.code == 22 || e.code == 1014) { // Webkit == 22 / Firefox == 1014 + _V_.log("LocalStorage Full (VideoJS)", e); + } else { + _V_.log("LocalStorage Error (VideoJS)", e); + } + } + } + +}); + +// usage: log('inside coolFunc', this, arguments); +// paulirish.com/2009/log-a-lightweight-wrapper-for-consolelog/ +_V_.log = function(){ + _V_.log.history = _V_.log.history || [];// store logs to an array for reference + _V_.log.history.push(arguments); + if(window.console) { + arguments.callee = arguments.callee.caller; + var newarr = [].slice.call(arguments); + (typeof console.log === 'object' ? _V_.log.apply.call(console.log, console, newarr) : console.log.apply(console, newarr)); + } +}; + +// make it safe to use console.log always +(function(b){function c(){}for(var d="assert,count,debug,dir,dirxml,error,exception,group,groupCollapsed,groupEnd,info,log,timeStamp,profile,profileEnd,time,timeEnd,trace,warn".split(","),a;a=d.pop();){b[a]=b[a]||c}})((function(){try +{console.log();return window.console;}catch(err){return window.console={};}})()); + +// Offset Left +// getBoundingClientRect technique from John Resig http://ejohn.org/blog/getboundingclientrect-is-awesome/ +if ("getBoundingClientRect" in document.documentElement) { + _V_.findPosX = function(el) { + var box; + + try { + box = el.getBoundingClientRect(); + } catch(e) {} + + if (!box) { return 0; } + + var docEl = document.documentElement, + body = document.body, + clientLeft = docEl.clientLeft || body.clientLeft || 0, + scrollLeft = window.pageXOffset || body.scrollLeft, + left = box.left + scrollLeft - clientLeft; + + return left; + }; +} else { + _V_.findPosX = function(el) { + var curleft = el.offsetLeft; + // _V_.log(obj.className, obj.offsetLeft) + while(el = obj.offsetParent) { + if (el.className.indexOf("video-js") == -1) { + // _V_.log(el.offsetParent, "OFFSETLEFT", el.offsetLeft) + // _V_.log("-webkit-full-screen", el.webkitMatchesSelector("-webkit-full-screen")); + // _V_.log("-webkit-full-screen", el.querySelectorAll(".video-js:-webkit-full-screen")); + } else { + } + curleft += el.offsetLeft; + } + return curleft; + }; +}// Using John Resig's Class implementation http://ejohn.org/blog/simple-javascript-inheritance/ +// (function(){var initializing=false, fnTest=/xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/; _V_.Class = function(){}; _V_.Class.extend = function(prop) { var _super = this.prototype; initializing = true; var prototype = new this(); initializing = false; for (var name in prop) { prototype[name] = typeof prop[name] == "function" && typeof _super[name] == "function" && fnTest.test(prop[name]) ? (function(name, fn){ return function() { var tmp = this._super; this._super = _super[name]; var ret = fn.apply(this, arguments); this._super = tmp; return ret; }; })(name, prop[name]) : prop[name]; } function Class() { if ( !initializing && this.init ) this.init.apply(this, arguments); } Class.prototype = prototype; Class.constructor = Class; Class.extend = arguments.callee; return Class;};})(); +(function(){ + var initializing = false, fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/; + _V_.Class = function(){}; + _V_.Class.extend = function(prop) { + var _super = this.prototype; + initializing = true; + var prototype = new this(); + initializing = false; + for (var name in prop) { + prototype[name] = typeof prop[name] == "function" && + typeof _super[name] == "function" && fnTest.test(prop[name]) ? + (function(name, fn){ + return function() { + var tmp = this._super; + this._super = _super[name]; + var ret = fn.apply(this, arguments); + this._super = tmp; + return ret; + }; + })(name, prop[name]) : + prop[name]; + } + function Class() { + if ( !initializing && this.init ) { + return this.init.apply(this, arguments); + + // Attempting to recreate accessing function form of class. + } else if (!initializing) { + return arguments.callee.prototype.init() + } + } + Class.prototype = prototype; + Class.constructor = Class; + Class.extend = arguments.callee; + return Class; + }; +})(); + +/* Player Component- Base class for all UI objects +================================================================================ */ +_V_.Component = _V_.Class.extend({ + + init: function(player, options){ + this.player = player; + + // Allow for overridding default component options + options = this.options = _V_.merge(this.options || {}, options); + + // Create element if one wasn't provided in options + if (options.el) { + this.el = options.el; + } else { + this.el = this.createElement(); + } + + // Add any components in options + this.initComponents(); + }, + + destroy: function(){}, + + createElement: function(type, attrs){ + return _V_.createElement(type || "div", attrs); + }, + + buildCSSClass: function(){ + // Child classes can include a function that does: + // return "CLASS NAME" + this._super(); + return ""; + }, + + initComponents: function(){ + var options = this.options; + if (options && options.components) { + // Loop through components and add them to the player + this.eachProp(options.components, function(name, opts){ + + // Allow waiting to add components until a specific event is called + var tempAdd = this.proxy(function(){ + this.addComponent(name, opts); + }); + + if (opts.loadEvent) { + this.one(opts.loadEvent, tempAdd) + } else { + tempAdd(); + } + }); + } + }, + + // Add child components to this component. + // Will generate a new child component and then append child component's element to this component's element. + // Takes either the name of the UI component class, or an object that contains a name, UI Class, and options. + addComponent: function(name, options){ + var componentClass, component; + + // Make sure options is at least an empty object to protect against errors + options = options || {}; + + // Assume name of set is a lowercased name of the UI Class (PlayButton, etc.) + componentClass = options.componentClass || _V_.capitalize(name); + + // Create a new object & element for this controls set + // If there's no .player, this is a player + component = new _V_[componentClass](this.player || this, options); + + // Add the UI object's element to the container div (box) + this.el.appendChild(component.el); + + // Set property name on player. Could cause conflicts with other prop names, but it's worth making refs easy. + this[name] = component; + }, + + /* Display + ================================================================================ */ + show: function(){ + this.el.style.display = "block"; + }, + + hide: function(){ + this.el.style.display = "none"; + }, + + fadeIn: function(){ + this.removeClass("vjs-fade-out"); + this.addClass("vjs-fade-in"); + }, + + fadeOut: function(){ + this.removeClass("vjs-fade-in"); + this.addClass("vjs-fade-out"); + }, + + addClass: function(classToAdd){ + _V_.addClass(this.el, classToAdd); + }, + + removeClass: function(classToRemove){ + _V_.removeClass(this.el, classToRemove); + }, + + /* Events + ================================================================================ */ + addEvent: function(type, fn){ + return _V_.addEvent(this.el, type, _V_.proxy(this, fn)); + }, + removeEvent: function(type, fn){ + return _V_.removeEvent(this.el, type, fn); + }, + triggerEvent: function(type, e){ + return _V_.triggerEvent(this.el, type, e); + }, + one: function(type, fn) { + _V_.one.call(this, this.el, type, fn); + }, + + /* Ready - Trigger functions when component is ready + ================================================================================ */ + ready: function(fn){ + if (!fn) return this; + + if (this.isReady) { + fn.call(this); + } else { + if (this.readyQueue === undefined) { + this.readyQueue = []; + } + this.readyQueue.push(fn); + } + + return this; + }, + + triggerReady: function(){ + this.isReady = true; + if (this.readyQueue && this.readyQueue.length > 0) { + // Call all functions in ready queue + this.each(this.readyQueue, function(fn){ + fn.call(this); + }); + + // Reset Ready Queue + this.readyQueue = []; + } + }, + + /* Utility + ================================================================================ */ + each: function(arr, fn){ _V_.each.call(this, arr, fn); }, + + eachProp: function(obj, fn){ _V_.eachProp.call(this, obj, fn); }, + + extend: function(obj){ _V_.merge(this, obj) }, + + // More easily attach 'this' to functions + proxy: function(fn){ return _V_.proxy(this, fn); } + +});/* Control - Base class for all control elements +================================================================================ */ +_V_.Control = _V_.Component.extend({ + + buildCSSClass: function(){ + return "vjs-control " + this._super(); + } + +}); + +/* Button - Base class for all buttons +================================================================================ */ +_V_.Button = _V_.Control.extend({ + + init: function(player, options){ + this._super(player, options); + + this.addEvent("click", this.onClick); + this.addEvent("focus", this.onFocus); + this.addEvent("blur", this.onBlur); + }, + + createElement: function(type, attrs){ + // Add standard Aria and Tabindex info + attrs = _V_.merge({ + className: this.buildCSSClass(), + innerHTML: '<div><span class="vjs-control-text">' + (this.buttonText || "Need Text") + '</span></div>', + role: "button", + tabIndex: 0 + }, attrs); + + return this._super(type, attrs); + }, + + // Click - Override with specific functionality for button + onClick: function(){}, + + // Focus - Add keyboard functionality to element + onFocus: function(){ + _V_.addEvent(document, "keyup", _V_.proxy(this, this.onKeyPress)); + }, + + // KeyPress (document level) - Trigger click when keys are pressed + onKeyPress: function(event){ + // Check for space bar (32) or enter (13) keys + if (event.which == 32 || event.which == 13) { + event.preventDefault(); + this.onClick(); + } + }, + + // Blur - Remove keyboard triggers + onBlur: function(){ + _V_.removeEvent(document, "keyup", _V_.proxy(this, this.onKeyPress)); + } + +}); + +/* Play Button +================================================================================ */ +_V_.PlayButton = _V_.Button.extend({ + + buttonText: "Play", + + buildCSSClass: function(){ + return "vjs-play-button " + this._super(); + }, + + onClick: function(){ + this.player.play(); + } + +}); + +/* Pause Button +================================================================================ */ +_V_.PauseButton = _V_.Button.extend({ + + buttonText: "Pause", + + buildCSSClass: function(){ + return "vjs-pause-button " + this._super(); + }, + + onClick: function(){ + this.player.pause(); + } + +}); + +/* Play Toggle - Play or Pause Media +================================================================================ */ +_V_.PlayToggle = _V_.Button.extend({ + + buttonText: "Play", + + init: function(player, options){ + this._super(player, options); + + player.addEvent("play", _V_.proxy(this, this.onPlay)); + player.addEvent("pause", _V_.proxy(this, this.onPause)); + }, + + buildCSSClass: function(){ + return "vjs-play-control " + this._super(); + }, + + // OnClick - Toggle between play and pause + onClick: function(){ + if (this.player.paused()) { + this.player.play(); + } else { + this.player.pause(); + } + }, + + // OnPlay - Add the vjs-playing class to the element so it can change appearance + onPlay: function(){ + _V_.removeClass(this.el, "vjs-paused"); + _V_.addClass(this.el, "vjs-playing"); + }, + + // OnPause - Add the vjs-paused class to the element so it can change appearance + onPause: function(){ + _V_.removeClass(this.el, "vjs-playing"); + _V_.addClass(this.el, "vjs-paused"); + } + +}); + + +/* Fullscreen Toggle Behaviors +================================================================================ */ +_V_.FullscreenToggle = _V_.Button.extend({ + + buttonText: "Fullscreen", + + buildCSSClass: function(){ + return "vjs-fullscreen-control " + this._super(); + }, + + onClick: function(){ + if (!this.player.isFullScreen) { + this.player.requestFullScreen(); + } else { + this.player.cancelFullScreen(); + } + } + +}); + +/* Big Play Button +================================================================================ */ +_V_.BigPlayButton = _V_.Button.extend({ + init: function(player, options){ + this._super(player, options); + + player.addEvent("play", _V_.proxy(this, this.hide)); + player.addEvent("ended", _V_.proxy(this, this.show)); + }, + + createElement: function(){ + return this._super("div", { + className: "vjs-big-play-button", + innerHTML: "<span></span>" + }); + }, + + onClick: function(){ + // Go back to the beginning if big play button is showing at the end. + // Have to check for current time otherwise it might throw a 'not ready' error. + if(this.player.currentTime()) { + this.player.currentTime(0); + } + this.player.play(); + } +}); + +/* Loading Spinner +================================================================================ */ +_V_.LoadingSpinner = _V_.Component.extend({ + init: function(player, options){ + this._super(player, options); + + player.addEvent("canplay", _V_.proxy(this, this.hide)); + player.addEvent("canplaythrough", _V_.proxy(this, this.hide)); + player.addEvent("playing", _V_.proxy(this, this.hide)); + + player.addEvent("seeking", _V_.proxy(this, this.show)); + player.addEvent("error", _V_.proxy(this, this.show)); + + // Not showing spinner on stalled any more. Browsers may stall and then not trigger any events that would remove the spinner. + // Checked in Chrome 16 and Safari 5.1.2. http://help.videojs.com/discussions/problems/883-why-is-the-download-progress-showing + // player.addEvent("stalled", _V_.proxy(this, this.show)); + + player.addEvent("waiting", _V_.proxy(this, this.show)); + }, + + createElement: function(){ + + var classNameSpinner, innerHtmlSpinner; + + if ( typeof this.player.el.style.WebkitBorderRadius == "string" + || typeof this.player.el.style.MozBorderRadius == "string" + || typeof this.player.el.style.KhtmlBorderRadius == "string" + || typeof this.player.el.style.borderRadius == "string") + { + classNameSpinner = "vjs-loading-spinner"; + innerHtmlSpinner = "<div class='ball1'></div><div class='ball2'></div><div class='ball3'></div><div class='ball4'></div><div class='ball5'></div><div class='ball6'></div><div class='ball7'></div><div class='ball8'></div>"; + } else { + classNameSpinner = "vjs-loading-spinner-fallback"; + innerHtmlSpinner = ""; + } + + return this._super("div", { + className: classNameSpinner, + innerHTML: innerHtmlSpinner + }); + } +}); + +/* Control Bar +================================================================================ */ +_V_.ControlBar = _V_.Component.extend({ + + options: { + loadEvent: "play", + components: { + "playToggle": {}, + "fullscreenToggle": {}, + "currentTimeDisplay": {}, + "timeDivider": {}, + "durationDisplay": {}, + "remainingTimeDisplay": {}, + "progressControl": {}, + "volumeControl": {}, + "muteToggle": {} + } + }, + + init: function(player, options){ + this._super(player, options); + + player.addEvent("play", this.proxy(function(){ + this.fadeIn(); + this.player.addEvent("mouseover", this.proxy(this.fadeIn)); + this.player.addEvent("mouseout", this.proxy(this.fadeOut)); + })); + }, + + createElement: function(){ + return _V_.createElement("div", { + className: "vjs-controls" + }); + }, + + fadeIn: function(){ + this._super(); + this.player.triggerEvent("controlsvisible"); + }, + + fadeOut: function(){ + this._super(); + this.player.triggerEvent("controlshidden"); + } +}); + +/* Time +================================================================================ */ +_V_.CurrentTimeDisplay = _V_.Component.extend({ + + init: function(player, options){ + this._super(player, options); + + player.addEvent("timeupdate", _V_.proxy(this, this.updateContent)); + }, + + createElement: function(){ + var el = this._super("div", { + className: "vjs-current-time vjs-time-controls vjs-control" + }); + + this.content = _V_.createElement("div", { + className: "vjs-current-time-display", + innerHTML: '0:00' + }); + + el.appendChild(_V_.createElement("div").appendChild(this.content)); + return el; + }, + + updateContent: function(){ + // Allows for smooth scrubbing, when player can't keep up. + var time = (this.player.scrubbing) ? this.player.values.currentTime : this.player.currentTime(); + this.content.innerHTML = _V_.formatTime(time, this.player.duration()); + } + +}); + +_V_.DurationDisplay = _V_.Component.extend({ + + init: function(player, options){ + this._super(player, options); + + player.addEvent("timeupdate", _V_.proxy(this, this.updateContent)); + }, + + createElement: function(){ + var el = this._super("div", { + className: "vjs-duration vjs-time-controls vjs-control" + }); + + this.content = _V_.createElement("div", { + className: "vjs-duration-display", + innerHTML: '0:00' + }); + + el.appendChild(_V_.createElement("div").appendChild(this.content)); + return el; + }, + + updateContent: function(){ + if (this.player.duration()) { this.content.innerHTML = _V_.formatTime(this.player.duration()); } + } + +}); + +// Time Separator (Not used in main skin, but still available, and could be used as a 'spare element') +_V_.TimeDivider = _V_.Component.extend({ + + createElement: function(){ + return this._super("div", { + className: "vjs-time-divider", + innerHTML: '<div><span>/</span></div>' + }); + } + +}); + +_V_.RemainingTimeDisplay = _V_.Component.extend({ + + init: function(player, options){ + this._super(player, options); + + player.addEvent("timeupdate", _V_.proxy(this, this.updateContent)); + }, + + createElement: function(){ + var el = this._super("div", { + className: "vjs-remaining-time vjs-time-controls vjs-control" + }); + + this.content = _V_.createElement("div", { + className: "vjs-remaining-time-display", + innerHTML: '-0:00' + }); + + el.appendChild(_V_.createElement("div").appendChild(this.content)); + return el; + }, + + updateContent: function(){ + if (this.player.duration()) { this.content.innerHTML = "-"+_V_.formatTime(this.player.remainingTime()); } + + // Allows for smooth scrubbing, when player can't keep up. + // var time = (this.player.scrubbing) ? this.player.values.currentTime : this.player.currentTime(); + // this.content.innerHTML = _V_.formatTime(time, this.player.duration()); + } + +}); + +/* Slider - Parent for seek bar and volume slider +================================================================================ */ +_V_.Slider = _V_.Component.extend({ + + init: function(player, options){ + this._super(player, options); + + player.addEvent(this.playerEvent, _V_.proxy(this, this.update)); + + this.addEvent("mousedown", this.onMouseDown); + this.addEvent("focus", this.onFocus); + this.addEvent("blur", this.onBlur); + + this.player.addEvent("controlsvisible", this.proxy(this.update)); + + // This is actually to fix the volume handle position. http://twitter.com/#!/gerritvanaaken/status/159046254519787520 + // this.player.one("timeupdate", this.proxy(this.update)); + + this.update(); + }, + + createElement: function(type, attrs) { + attrs = _V_.merge({ + role: "slider", + "aria-valuenow": 0, + "aria-valuemin": 0, + "aria-valuemax": 100, + tabIndex: 0 + }, attrs); + + return this._super(type, attrs); + }, + + onMouseDown: function(event){ + event.preventDefault(); + _V_.blockTextSelection(); + + _V_.addEvent(document, "mousemove", _V_.proxy(this, this.onMouseMove)); + _V_.addEvent(document, "mouseup", _V_.proxy(this, this.onMouseUp)); + + this.onMouseMove(event); + }, + + onMouseUp: function(event) { + _V_.unblockTextSelection(); + _V_.removeEvent(document, "mousemove", this.onMouseMove, false); + _V_.removeEvent(document, "mouseup", this.onMouseUp, false); + + this.update(); + }, + + update: function(){ + // If scrubbing, we could use a cached value to make the handle keep up with the user's mouse. + // On HTML5 browsers scrubbing is really smooth, but some flash players are slow, so we might want to utilize this later. + // var progress = (this.player.scrubbing) ? this.player.values.currentTime / this.player.duration() : this.player.currentTime() / this.player.duration(); + + var barProgress, + progress = this.getPercent(); + handle = this.handle, + bar = this.bar; + + // Protect against no duration and other division issues + if (isNaN(progress)) { progress = 0; } + + barProgress = progress; + + // If there is a handle, we need to account for the handle in our calculation for progress bar + // so that it doesn't fall short of or extend past the handle. + if (handle) { + + var box = this.el, + boxWidth = box.offsetWidth, + + handleWidth = handle.el.offsetWidth, + + // The width of the handle in percent of the containing box + // In IE, widths may not be ready yet causing NaN + handlePercent = (handleWidth) ? handleWidth / boxWidth : 0, + + // Get the adjusted size of the box, considering that the handle's center never touches the left or right side. + // There is a margin of half the handle's width on both sides. + boxAdjustedPercent = 1 - handlePercent; + + // Adjust the progress that we'll use to set widths to the new adjusted box width + adjustedProgress = progress * boxAdjustedPercent, + + // The bar does reach the left side, so we need to account for this in the bar's width + barProgress = adjustedProgress + (handlePercent / 2); + + // Move the handle from the left based on the adjected progress + handle.el.style.left = _V_.round(adjustedProgress * 100, 2) + "%"; + } + + // Set the new bar width + bar.el.style.width = _V_.round(barProgress * 100, 2) + "%"; + }, + + calculateDistance: function(event){ + var box = this.el, + boxX = _V_.findPosX(box), + boxW = box.offsetWidth, + handle = this.handle; + + if (handle) { + var handleW = handle.el.offsetWidth; + + // Adjusted X and Width, so handle doesn't go outside the bar + boxX = boxX + (handleW / 2); + boxW = boxW - handleW; + } + + // Percent that the click is through the adjusted area + return Math.max(0, Math.min(1, (event.pageX - boxX) / boxW)); + }, + + onFocus: function(event){ + _V_.addEvent(document, "keyup", _V_.proxy(this, this.onKeyPress)); + }, + + onKeyPress: function(event){ + if (event.which == 37) { // Left Arrow + event.preventDefault(); + this.stepBack(); + } else if (event.which == 39) { // Right Arrow + event.preventDefault(); + this.stepForward(); + } + }, + + onBlur: function(event){ + _V_.removeEvent(document, "keyup", _V_.proxy(this, this.onKeyPress)); + } +}); + + +/* Progress +================================================================================ */ + +// Progress Control: Seek, Load Progress, and Play Progress +_V_.ProgressControl = _V_.Component.extend({ + + options: { + components: { + "seekBar": {} + } + }, + + createElement: function(){ + return this._super("div", { + className: "vjs-progress-control vjs-control" + }); + } + +}); + +// Seek Bar and holder for the progress bars +_V_.SeekBar = _V_.Slider.extend({ + + options: { + components: { + "loadProgressBar": {}, + + // Set property names to bar and handle to match with the parent Slider class is looking for + "bar": { componentClass: "PlayProgressBar" }, + "handle": { componentClass: "SeekHandle" } + } + }, + + playerEvent: "timeupdate", + + init: function(player, options){ + this._super(player, options); + }, + + createElement: function(){ + return this._super("div", { + className: "vjs-progress-holder" + }); + }, + + getPercent: function(){ + return this.player.currentTime() / this.player.duration(); + }, + + onMouseDown: function(event){ + this._super(event); + + this.player.scrubbing = true; + + this.videoWasPlaying = !this.player.paused(); + this.player.pause(); + }, + + onMouseMove: function(event){ + var newTime = this.calculateDistance(event) * this.player.duration(); + + // Don't let video end while scrubbing. + if (newTime == this.player.duration()) { newTime = newTime - 0.1; } + + // Set new time (tell player to seek to new time) + this.player.currentTime(newTime); + }, + + onMouseUp: function(event){ + this._super(event); + + this.player.scrubbing = false; + if (this.videoWasPlaying) { + this.player.play(); + } + }, + + stepForward: function(){ + this.player.currentTime(this.player.currentTime() + 1); + }, + + stepBack: function(){ + this.player.currentTime(this.player.currentTime() - 1); + } + +}); + +// Load Progress Bar +_V_.LoadProgressBar = _V_.Component.extend({ + + init: function(player, options){ + this._super(player, options); + player.addEvent("progress", _V_.proxy(this, this.update)); + }, + + createElement: function(){ + return this._super("div", { + className: "vjs-load-progress", + innerHTML: '<span class="vjs-control-text">Loaded: 0%</span>' + }); + }, + + update: function(){ + if (this.el.style) { this.el.style.width = _V_.round(this.player.bufferedPercent() * 100, 2) + "%"; } + } + +}); + +// Play Progress Bar +_V_.PlayProgressBar = _V_.Component.extend({ + + createElement: function(){ + return this._super("div", { + className: "vjs-play-progress", + innerHTML: '<span class="vjs-control-text">Progress: 0%</span>' + }); + } + +}); + +// Seek Handle +// SeekBar Behavior includes play progress bar, and seek handle +// Needed so it can determine seek position based on handle position/size +_V_.SeekHandle = _V_.Component.extend({ + + createElement: function(){ + return this._super("div", { + className: "vjs-seek-handle", + innerHTML: '<span class="vjs-control-text">00:00</span>' + }); + } + +}); + +/* Volume Scrubber +================================================================================ */ +// Progress Control: Seek, Load Progress, and Play Progress +_V_.VolumeControl = _V_.Component.extend({ + + options: { + components: { + "volumeBar": {} + } + }, + + createElement: function(){ + return this._super("div", { + className: "vjs-volume-control vjs-control" + }); + } + +}); + +_V_.VolumeBar = _V_.Slider.extend({ + + options: { + components: { + "bar": { componentClass: "VolumeLevel" }, + "handle": { componentClass: "VolumeHandle" } + } + }, + + playerEvent: "volumechange", + + createElement: function(){ + return this._super("div", { + className: "vjs-volume-bar" + }); + }, + + onMouseMove: function(event) { + this.player.volume(this.calculateDistance(event)); + }, + + getPercent: function(){ + return this.player.volume(); + }, + + stepForward: function(){ + this.player.volume(this.player.volume() + 0.1); + }, + + stepBack: function(){ + this.player.volume(this.player.volume() - 0.1); + } +}); + +_V_.VolumeLevel = _V_.Component.extend({ + + createElement: function(){ + return this._super("div", { + className: "vjs-volume-level", + innerHTML: '<span class="vjs-control-text"></span>' + }); + } + +}); + +_V_.VolumeHandle = _V_.Component.extend({ + + createElement: function(){ + return this._super("div", { + className: "vjs-volume-handle", + innerHTML: '<span class="vjs-control-text"></span>' + // tabindex: 0, + // role: "slider", "aria-valuenow": 0, "aria-valuemin": 0, "aria-valuemax": 100 + }); + } + +}); + +_V_.MuteToggle = _V_.Button.extend({ + + init: function(player, options){ + this._super(player, options); + + player.addEvent("volumechange", _V_.proxy(this, this.update)); + }, + + createElement: function(){ + return this._super("div", { + className: "vjs-mute-control vjs-control", + innerHTML: '<div><span class="vjs-control-text">Mute</span></div>' + }); + }, + + onClick: function(event){ + this.player.muted( this.player.muted() ? false : true ); + }, + + update: function(event){ + var vol = this.player.volume(), + level = 3; + + if (vol == 0 || this.player.muted()) { + level = 0; + } else if (vol < 0.33) { + level = 1; + } else if (vol < 0.67) { + level = 2; + } + + /* TODO improve muted icon classes */ + _V_.each.call(this, [0,1,2,3], function(i){ + _V_.removeClass(this.el, "vjs-vol-"+i); + }); + _V_.addClass(this.el, "vjs-vol-"+level); + } + +}); + + +/* Poster Image +================================================================================ */ +_V_.Poster = _V_.Button.extend({ + init: function(player, options){ + this._super(player, options); + + if (!this.player.options.poster) { + this.hide(); + } + + player.addEvent("play", _V_.proxy(this, this.hide)); + }, + + createElement: function(){ + return _V_.createElement("img", { + className: "vjs-poster", + src: this.player.options.poster, + + // Don't want poster to be tabbable. + tabIndex: -1 + }); + }, + + onClick: function(){ + this.player.play(); + } +}); + + +/* Text Track Displays +================================================================================ */ +// Create a behavior type for each text track type (subtitlesDisplay, captionsDisplay, etc.). +// Then you can easily do something like. +// player.addBehavior(myDiv, "subtitlesDisplay"); +// And the myDiv's content will be updated with the text change. + +// Base class for all track displays. Should not be instantiated on its own. +_V_.TextTrackDisplay = _V_.Component.extend({ + + init: function(player, options){ + this._super(player, options); + + player.addEvent(this.trackType + "update", _V_.proxy(this, this.update)); + }, + + createElement: function(){ + return this._super("div", { + className: "vjs-" + this.trackType + }); + }, + + update: function(){ + this.el.innerHTML = this.player.textTrackValue(this.trackType); + } + +}); + +_V_.SubtitlesDisplay = _V_.TextTrackDisplay.extend({ + + trackType: "subtitles" + +}); + +_V_.CaptionsDisplay = _V_.TextTrackDisplay.extend({ + + trackType: "captions" + +}); + +_V_.ChaptersDisplay = _V_.TextTrackDisplay.extend({ + + trackType: "chapters" + +}); + +_V_.DescriptionsDisplay = _V_.TextTrackDisplay.extend({ + + trackType: "descriptions" + +});// ECMA-262 is the standard for javascript. +// The following methods are impelemented EXACTLY as described in the standard (according to Mozilla Docs), and do not override the default method if one exists. +// This may conflict with other libraries that modify the array prototype, but those libs should update to use the standard. + +// [].indexOf +// https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/indexOf +if (!Array.prototype.indexOf) { + Array.prototype.indexOf = function (searchElement /*, fromIndex */ ) { + "use strict"; + if (this === void 0 || this === null) { + throw new TypeError(); + } + var t = Object(this); + var len = t.length >>> 0; + if (len === 0) { + return -1; + } + var n = 0; + if (arguments.length > 0) { + n = Number(arguments[1]); + if (n !== n) { // shortcut for verifying if it's NaN + n = 0; + } else if (n !== 0 && n !== (1 / 0) && n !== -(1 / 0)) { + n = (n > 0 || -1) * Math.floor(Math.abs(n)); + } + } + if (n >= len) { + return -1; + } + var k = n >= 0 ? n : Math.max(len - Math.abs(n), 0); + for (; k < len; k++) { + if (k in t && t[k] === searchElement) { + return k; + } + } + return -1; + } +} + +// NOT NEEDED YET +// [].lastIndexOf +// https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/lastIndexOf +// if (!Array.prototype.lastIndexOf) +// { +// Array.prototype.lastIndexOf = function(searchElement /*, fromIndex*/) +// { +// "use strict"; +// +// if (this === void 0 || this === null) +// throw new TypeError(); +// +// var t = Object(this); +// var len = t.length >>> 0; +// if (len === 0) +// return -1; +// +// var n = len; +// if (arguments.length > 1) +// { +// n = Number(arguments[1]); +// if (n !== n) +// n = 0; +// else if (n !== 0 && n !== (1 / 0) && n !== -(1 / 0)) +// n = (n > 0 || -1) * Math.floor(Math.abs(n)); +// } +// +// var k = n >= 0 +// ? Math.min(n, len - 1) +// : len - Math.abs(n); +// +// for (; k >= 0; k--) +// { +// if (k in t && t[k] === searchElement) +// return k; +// } +// return -1; +// }; +// } + + +// NOT NEEDED YET +// Array forEach per ECMA standard https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/array/foreach +// Production steps of ECMA-262, Edition 5, 15.4.4.18 +// Reference: http://es5.github.com/#x15.4.4.18 +// if ( !Array.prototype.forEach ) { +// +// Array.prototype.forEach = function( callback, thisArg ) { +// +// var T, k; +// +// if ( this == null ) { +// throw new TypeError( " this is null or not defined" ); +// } +// +// // 1. Let O be the result of calling ToObject passing the |this| value as the argument. +// var O = Object(this); +// +// // 2. Let lenValue be the result of calling the Get internal method of O with the argument "length". +// // 3. Let len be ToUint32(lenValue). +// var len = O.length >>> 0; +// +// // 4. If IsCallable(callback) is false, throw a TypeError exception. +// // See: http://es5.github.com/#x9.11 +// if ( {}.toString.call(callback) != "[object Function]" ) { +// throw new TypeError( callback + " is not a function" ); +// } +// +// // 5. If thisArg was supplied, let T be thisArg; else let T be undefined. +// if ( thisArg ) { +// T = thisArg; +// } +// +// // 6. Let k be 0 +// k = 0; +// +// // 7. Repeat, while k < len +// while( k < len ) { +// +// var kValue; +// +// // a. Let Pk be ToString(k). +// // This is implicit for LHS operands of the in operator +// // b. Let kPresent be the result of calling the HasProperty internal method of O with argument Pk. +// // This step can be combined with c +// // c. If kPresent is true, then +// if ( k in O ) { +// +// // i. Let kValue be the result of calling the Get internal method of O with argument Pk. +// kValue = O[ Pk ]; +// +// // ii. Call the Call internal method of callback with T as the this value and +// // argument list containing kValue, k, and O. +// callback.call( T, kValue, k, O ); +// } +// // d. Increase k by 1. +// k++; +// } +// // 8. return undefined +// }; +// } + + +// NOT NEEDED YET +// https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/map +// Production steps of ECMA-262, Edition 5, 15.4.4.19 +// Reference: http://es5.github.com/#x15.4.4.19 +// if (!Array.prototype.map) { +// Array.prototype.map = function(callback, thisArg) { +// +// var T, A, k; +// +// if (this == null) { +// throw new TypeError(" this is null or not defined"); +// } +// +// // 1. Let O be the result of calling ToObject passing the |this| value as the argument. +// var O = Object(this); +// +// // 2. Let lenValue be the result of calling the Get internal method of O with the argument "length". +// // 3. Let len be ToUint32(lenValue). +// var len = O.length >>> 0; +// +// // 4. If IsCallable(callback) is false, throw a TypeError exception. +// // See: http://es5.github.com/#x9.11 +// if ({}.toString.call(callback) != "[object Function]") { +// throw new TypeError(callback + " is not a function"); +// } +// +// // 5. If thisArg was supplied, let T be thisArg; else let T be undefined. +// if (thisArg) { +// T = thisArg; +// } +// +// // 6. Let A be a new array created as if by the expression new Array(len) where Array is +// // the standard built-in constructor with that name and len is the value of len. +// A = new Array(len); +// +// // 7. Let k be 0 +// k = 0; +// +// // 8. Repeat, while k < len +// while(k < len) { +// +// var kValue, mappedValue; +// +// // a. Let Pk be ToString(k). +// // This is implicit for LHS operands of the in operator +// // b. Let kPresent be the result of calling the HasProperty internal method of O with argument Pk. +// // This step can be combined with c +// // c. If kPresent is true, then +// if (k in O) { +// +// // i. Let kValue be the result of calling the Get internal method of O with argument Pk. +// kValue = O[ k ]; +// +// // ii. Let mappedValue be the result of calling the Call internal method of callback +// // with T as the this value and argument list containing kValue, k, and O. +// mappedValue = callback.call(T, kValue, k, O); +// +// // iii. Call the DefineOwnProperty internal method of A with arguments +// // Pk, Property Descriptor {Value: mappedValue, Writable: true, Enumerable: true, Configurable: true}, +// // and false. +// +// // In browsers that support Object.defineProperty, use the following: +// // Object.defineProperty(A, Pk, { value: mappedValue, writable: true, enumerable: true, configurable: true }); +// +// // For best browser support, use the following: +// A[ k ] = mappedValue; +// } +// // d. Increase k by 1. +// k++; +// } +// +// // 9. return A +// return A; +// }; +// } +// Event System (J.Resig - Secrets of a JS Ninja http://jsninja.com/ [Go read it, really]) +// (Book version isn't completely usable, so fixed some things and borrowed from jQuery where it's working) +// +// This should work very similarly to jQuery's events, however it's based off the book version which isn't as +// robust as jquery's, so there's probably some differences. +// +// When you add an event listener using _V_.addEvent, +// it stores the handler function in seperate cache object, +// and adds a generic handler to the element's event, +// along with a unique id (guid) to the element. + +_V_.extend({ + + // Add an event listener to element + // It stores the handler function in a separate cache object + // and adds a generic handler to the element's event, + // along with a unique id (guid) to the element. + addEvent: function(elem, type, fn){ + var data = _V_.getData(elem), handlers; + + // We only need to generate one handler per element + if (data && !data.handler) { + // Our new meta-handler that fixes the event object and the context + data.handler = function(event){ + event = _V_.fixEvent(event); + var handlers = _V_.getData(elem).events[event.type]; + // Go through and call all the real bound handlers + if (handlers) { + + // Copy handlers so if handlers are added/removed during the process it doesn't throw everything off. + var handlersCopy = []; + _V_.each(handlers, function(handler, i){ + handlersCopy[i] = handler; + }) + + for (var i = 0, l = handlersCopy.length; i < l; i++) { + handlersCopy[i].call(elem, event); + } + } + }; + } + + // We need a place to store all our event data + if (!data.events) { data.events = {}; } + + // And a place to store the handlers for this event type + handlers = data.events[type]; + + if (!handlers) { + handlers = data.events[ type ] = []; + + // Attach our meta-handler to the element, since one doesn't exist + if (document.addEventListener) { + elem.addEventListener(type, data.handler, false); + } else if (document.attachEvent) { + elem.attachEvent("on" + type, data.handler); + } + } + + if (!fn.guid) { fn.guid = _V_.guid++; } + + handlers.push(fn); + }, + + removeEvent: function(elem, type, fn) { + var data = _V_.getData(elem), handlers; + // If no events exist, nothing to unbind + if (!data.events) { return; } + + // Are we removing all bound events? + if (!type) { + for (type in data.events) { + _V_.cleanUpEvents(elem, type); + } + return; + } + + // And a place to store the handlers for this event type + handlers = data.events[type]; + + // If no handlers exist, nothing to unbind + if (!handlers) { return; } + + // See if we're only removing a single handler + if (fn && fn.guid) { + for (var i = 0; i < handlers.length; i++) { + // We found a match (don't stop here, there could be a couple bound) + if (handlers[i].guid === fn.guid) { + // Remove the handler from the array of handlers + handlers.splice(i--, 1); + } + } + } + + _V_.cleanUpEvents(elem, type); + }, + + cleanUpEvents: function(elem, type) { + var data = _V_.getData(elem); + // Remove the events of a particular type if there are none left + + if (data.events[type].length === 0) { + delete data.events[type]; + + // Remove the meta-handler from the element + if (document.removeEventListener) { + elem.removeEventListener(type, data.handler, false); + } else if (document.detachEvent) { + elem.detachEvent("on" + type, data.handler); + } + } + + // Remove the events object if there are no types left + if (_V_.isEmpty(data.events)) { + delete data.events; + delete data.handler; + } + + // Finally remove the expando if there is no data left + if (_V_.isEmpty(data)) { + _V_.removeData(elem); + } + }, + + fixEvent: function(event) { + if (event[_V_.expando]) { return event; } + // store a copy of the original event object + // and "clone" to set read-only properties + var originalEvent = event; + event = new _V_.Event(originalEvent); + + for ( var i = _V_.Event.props.length, prop; i; ) { + prop = _V_.Event.props[ --i ]; + event[prop] = originalEvent[prop]; + } + + // Fix target property, if necessary + if (!event.target) { event.target = event.srcElement || document; } + + // check if target is a textnode (safari) + if (event.target.nodeType === 3) { event.target = event.target.parentNode; } + + // Add relatedTarget, if necessary + if (!event.relatedTarget && event.fromElement) { + event.relatedTarget = event.fromElement === event.target ? event.toElement : event.fromElement; + } + + // Calculate pageX/Y if missing and clientX/Y available + if ( event.pageX == null && event.clientX != null ) { + var eventDocument = event.target.ownerDocument || document, + doc = eventDocument.documentElement, + body = eventDocument.body; + + event.pageX = event.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc && doc.clientLeft || body && body.clientLeft || 0); + event.pageY = event.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) - (doc && doc.clientTop || body && body.clientTop || 0); + } + + // Add which for key events + if (event.which == null && (event.charCode != null || event.keyCode != null)) { + event.which = event.charCode != null ? event.charCode : event.keyCode; + } + + // Add metaKey to non-Mac browsers (use ctrl for PC's and Meta for Macs) + if ( !event.metaKey && event.ctrlKey ) { + event.metaKey = event.ctrlKey; + } + + // Add which for click: 1 === left; 2 === middle; 3 === right + // Note: button is not normalized, so don't use it + if ( !event.which && event.button !== undefined ) { + event.which = (event.button & 1 ? 1 : ( event.button & 2 ? 3 : ( event.button & 4 ? 2 : 0 ) )); + } + + return event; + }, + + triggerEvent: function(elem, event) { + var data = _V_.getData(elem), + parent = elem.parentNode || elem.ownerDocument, + type = event.type || event, + handler; + + if (data) { handler = data.handler } + + // Added in attion to book. Book code was broke. + event = typeof event === "object" ? + event[_V_.expando] ? + event : + new _V_.Event(type, event) : + new _V_.Event(type); + + event.type = type; + if (handler) { + handler.call(elem, event); + } + + // Clean up the event in case it is being reused + event.result = undefined; + event.target = elem; + + // Bubble the event up the tree to the document, + // Unless it's been explicitly stopped + // if (parent && !event.isPropagationStopped()) { + // _V_.triggerEvent(parent, event); + // + // // We're at the top document so trigger the default action + // } else if (!parent && !event.isDefaultPrevented()) { + // // log(type); + // var targetData = _V_.getData(event.target); + // // log(targetData); + // var targetHandler = targetData.handler; + // // log("2"); + // if (event.target[event.type]) { + // // Temporarily disable the bound handler, + // // don't want to execute it twice + // if (targetHandler) { + // targetData.handler = function(){}; + // } + // + // // Trigger the native event (click, focus, blur) + // event.target[event.type](); + // + // // Restore the handler + // if (targetHandler) { + // targetData.handler = targetHandler; + // } + // } + // } + }, + + one: function(elem, type, fn) { + _V_.addEvent(elem, type, function(){ + _V_.removeEvent(elem, type, arguments.callee) + fn.apply(this, arguments); + }); + } +}); + +// Custom Event object for standardizing event objects between browsers. +_V_.Event = function(src, props){ + // Event object + if (src && src.type) { + this.originalEvent = src; + this.type = src.type; + + // Events bubbling up the document may have been marked as prevented + // by a handler lower down the tree; reflect the correct value. + this.isDefaultPrevented = (src.defaultPrevented || src.returnValue === false || + src.getPreventDefault && src.getPreventDefault()) ? returnTrue : returnFalse; + + // Event type + } else { + this.type = src; + } + + // Put explicitly provided properties onto the event object + if (props) { _V_.merge(this, props); } + + this.timeStamp = (new Date).getTime(); + + // Mark it as fixed + this[_V_.expando] = true; +}; + +_V_.Event.prototype = { + preventDefault: function() { + this.isDefaultPrevented = returnTrue; + + var e = this.originalEvent; + if (!e) { return; } + + // if preventDefault exists run it on the original event + if (e.preventDefault) { + e.preventDefault(); + // otherwise set the returnValue property of the original event to false (IE) + } else { + e.returnValue = false; + } + }, + stopPropagation: function() { + this.isPropagationStopped = returnTrue; + + var e = this.originalEvent; + if (!e) { return; } + // if stopPropagation exists run it on the original event + if (e.stopPropagation) { e.stopPropagation(); } + // otherwise set the cancelBubble property of the original event to true (IE) + e.cancelBubble = true; + }, + stopImmediatePropagation: function() { + this.isImmediatePropagationStopped = returnTrue; + this.stopPropagation(); + }, + isDefaultPrevented: returnFalse, + isPropagationStopped: returnFalse, + isImmediatePropagationStopped: returnFalse +}; +_V_.Event.props = "altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode metaKey newValue offsetX offsetY pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "); + +function returnTrue(){ return true; } +function returnFalse(){ return false; } + +// Javascript JSON implementation +// (Parse Method Only) +// https://github.com/douglascrockford/JSON-js/blob/master/json2.js + +var JSON; +if (!JSON) { JSON = {}; } + +(function(){ + var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g; + + if (typeof JSON.parse !== 'function') { + JSON.parse = function (text, reviver) { + var j; + + function walk(holder, key) { + var k, v, value = holder[key]; + if (value && typeof value === 'object') { + for (k in value) { + if (Object.prototype.hasOwnProperty.call(value, k)) { + v = walk(value, k); + if (v !== undefined) { + value[k] = v; + } else { + delete value[k]; + } + } + } + } + return reviver.call(holder, key, value); + } + text = String(text); + cx.lastIndex = 0; + if (cx.test(text)) { + text = text.replace(cx, function (a) { + return '\\u' + + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); + }); + } + + if (/^[\],:{}\s]*$/ + .test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@') + .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']') + .replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) { + + j = eval('(' + text + ')'); + + return typeof reviver === 'function' ? + walk({'': j}, '') : j; + } + + throw new SyntaxError('JSON.parse'); + }; + } +}()); +/* UI Component- Base class for all UI objects +================================================================================ */ +_V_.Player = _V_.Component.extend({ + + init: function(tag, addOptions, ready){ + + this.tag = tag; // Store the original tag used to set options + + var el = this.el = _V_.createElement("div"), // Div to contain video and controls + options = this.options = {}, + width = options.width = tag.getAttribute('width'), + height = options.height = tag.getAttribute('height'), + + // Browsers default to 300x150 if there's no width/height or video size data. + initWidth = width || 300, + initHeight = height || 150; + + // Make player findable on elements + tag.player = el.player = this; + + // Add callback to ready queue + this.ready(ready); + + // Wrap video tag in div (el/box) container + tag.parentNode.insertBefore(el, tag); + el.appendChild(tag); // Breaks iPhone, fixed in HTML5 setup. + + // Give video tag properties to box + el.id = this.id = tag.id; // ID will now reference box, not the video tag + el.className = tag.className; + // Update tag id/class for use as HTML5 playback tech + tag.id += "_html5_api"; + tag.className = "vjs-tech"; + + // Make player easily findable by ID + _V_.players[el.id] = this; + + // Make box use width/height of tag, or default 300x150 + el.setAttribute("width", initWidth); + el.setAttribute("height", initHeight); + // Enforce with CSS since width/height attrs don't work on divs + el.style.width = initWidth+"px"; + el.style.height = initHeight+"px"; + // Remove width/height attrs from tag so CSS can make it 100% width/height + tag.removeAttribute("width"); + tag.removeAttribute("height"); + + // Set Options + _V_.merge(options, _V_.options); // Copy Global Defaults + _V_.merge(options, this.getVideoTagSettings()); // Override with Video Tag Options + _V_.merge(options, addOptions); // Override/extend with options from setup call + + // Store controls setting, and then remove immediately so native controls don't flash. + tag.removeAttribute("controls"); + + // Poster will be handled by a manual <img> + tag.removeAttribute("poster"); + + // Empty video tag sources and tracks so the built in player doesn't use them also. + if (tag.hasChildNodes()) { + for (var i=0,j=tag.childNodes;i<j.length;i++) { + if (j[i].nodeName == "SOURCE" || j[i].nodeName == "TRACK") { + tag.removeChild(j[i]); + } + } + } + + // Holder for playback tech components + this.techs = {}; + + // Cache for video property values. + this.values = {}; + + this.addClass("vjs-paused"); + + this.addEvent("ended", this.onEnded); + this.addEvent("play", this.onPlay); + this.addEvent("pause", this.onPause); + this.addEvent("error", this.onError); + + // When the API is ready, loop through the components and add to the player. + if (options.controls) { + this.ready(function(){ + this.initComponents(); + }); + } + + // If there are no sources when the player is initialized, + // load the first supported playback technology. + if (!options.sources || options.sources.length == 0) { + for (var i=0,j=options.techOrder; i<j.length; i++) { + var techName = j[i], + tech = _V_[techName]; + + // Check if the browser supports this technology + if (tech.isSupported()) { + this.loadTech(techName); + break; + } + } + } else { + // Loop through playback technologies (HTML5, Flash) and check for support + // Then load the best source. + this.src(options.sources); + } + }, + + // Cache for video property values. + values: {}, + + destroy: function(){ + // Ensure that tracking progress and time progress will stop and plater deleted + this.stopTrackingProgress(); + this.stopTrackingCurrentTime(); + delete _V_.players[this.id] + }, + + createElement: function(type, options){ + + }, + + getVideoTagSettings: function(){ + var options = { + sources: [], + tracks: [] + }; + + options.src = this.tag.getAttribute("src"); + options.controls = this.tag.getAttribute("controls") !== null; + options.poster = this.tag.getAttribute("poster"); + options.preload = this.tag.getAttribute("preload"); + options.autoplay = this.tag.getAttribute("autoplay") !== null; // hasAttribute not IE <8 compatible + options.loop = this.tag.getAttribute("loop") !== null; + options.muted = this.tag.getAttribute("muted") !== null; + + if (this.tag.hasChildNodes()) { + for (var c,i=0,j=this.tag.childNodes;i<j.length;i++) { + c = j[i]; + if (c.nodeName == "SOURCE") { + options.sources.push({ + src: c.getAttribute('src'), + type: c.getAttribute('type'), + media: c.getAttribute('media'), + title: c.getAttribute('title') + }); + } + if (c.nodeName == "TRACK") { + options.tracks.push(new _V_.Track({ + src: c.getAttribute("src"), + kind: c.getAttribute("kind"), + srclang: c.getAttribute("srclang"), + label: c.getAttribute("label"), + 'default': c.getAttribute("default") !== null, + title: c.getAttribute("title") + }, this)); + + } + } + } + return options; + }, + + /* PLayback Technology (tech) + ================================================================================ */ + // Load/Create an instance of playback technlogy including element and API methods + // And append playback element in player div. + loadTech: function(techName, source){ + + // Pause and remove current playback technology + if (this.tech) { + this.unloadTech(); + + // If the first time loading, HTML5 tag will exist but won't be initialized + // So we need to remove it if we're not loading HTML5 + } else if (techName != "html5" && this.tag) { + this.el.removeChild(this.tag); + this.tag = false; + } + + this.techName = techName; + + // Turn off API access because we're loading a new tech that might load asynchronously + this.isReady = false; + + var techReady = function(){ + this.player.triggerReady(); + + // Manually track progress in cases where the browser/flash player doesn't report it. + if (!this.support.progressEvent) { + this.player.manualProgressOn(); + } + + // Manually track timeudpates in cases where the browser/flash player doesn't report it. + if (!this.support.timeupdateEvent) { + this.player.manualTimeUpdatesOn(); + } + } + + // Grab tech-specific options from player options and add source and parent element to use. + var techOptions = _V_.merge({ source: source, parentEl: this.el }, this.options[techName]) + + if (source) { + if (source.src == this.values.src && this.values.currentTime > 0) { + techOptions.startTime = this.values.currentTime; + } + + this.values.src = source.src; + } + + // Initialize tech instance + this.tech = new _V_[techName](this, techOptions); + this.tech.ready(techReady); + }, + + unloadTech: function(){ + this.tech.destroy(); + + // Turn off any manual progress or timeupdate tracking + if (this.manualProgress) { this.manualProgressOff(); } + + if (this.manualTimeUpdates) { this.manualTimeUpdatesOff(); } + + this.tech = false; + }, + + // There's many issues around changing the size of a Flash (or other plugin) object. + // First is a plugin reload issue in Firefox that has been around for 11 years: https://bugzilla.mozilla.org/show_bug.cgi?id=90268 + // Then with the new fullscreen API, Mozilla and webkit browsers will reload the flash object after going to fullscreen. + // To get around this, we're unloading the tech, caching source and currentTime values, and reloading the tech once the plugin is resized. + // reloadTech: function(betweenFn){ + // _V_.log("unloadingTech") + // this.unloadTech(); + // _V_.log("unloadedTech") + // if (betweenFn) { betweenFn.call(); } + // _V_.log("LoadingTech") + // this.loadTech(this.techName, { src: this.values.src }) + // _V_.log("loadedTech") + // }, + + /* Fallbacks for unsupported event types + ================================================================================ */ + // Manually trigger progress events based on changes to the buffered amount + // Many flash players and older HTML5 browsers don't send progress or progress-like events + manualProgressOn: function(){ + this.manualProgress = true; + + // Trigger progress watching when a source begins loading + this.trackProgress(); + + // Watch for a native progress event call on the tech element + // In HTML5, some older versions don't support the progress event + // So we're assuming they don't, and turning off manual progress if they do. + this.tech.addEvent("progress", function(){ + + // Remove this listener from the element + this.removeEvent("progress", arguments.callee); + + // Update known progress support for this playback technology + this.support.progressEvent = true; + + // Turn off manual progress tracking + this.player.manualProgressOff(); + }); + }, + + manualProgressOff: function(){ + this.manualProgress = false; + this.stopTrackingProgress(); + }, + + trackProgress: function(){ + this.progressInterval = setInterval(_V_.proxy(this, function(){ + // Don't trigger unless buffered amount is greater than last time + // log(this.values.bufferEnd, this.buffered().end(0), this.duration()) + /* TODO: update for multiple buffered regions */ + if (this.values.bufferEnd < this.buffered().end(0)) { + this.triggerEvent("progress"); + } else if (this.bufferedPercent() == 1) { + this.stopTrackingProgress(); + this.triggerEvent("progress"); // Last update + } + }), 500); + }, + stopTrackingProgress: function(){ clearInterval(this.progressInterval); }, + + /* Time Tracking -------------------------------------------------------------- */ + manualTimeUpdatesOn: function(){ + this.manualTimeUpdates = true; + + this.addEvent("play", this.trackCurrentTime); + this.addEvent("pause", this.stopTrackingCurrentTime); + // timeupdate is also called by .currentTime whenever current time is set + + // Watch for native timeupdate event + this.tech.addEvent("timeupdate", function(){ + + // Remove this listener from the element + this.removeEvent("timeupdate", arguments.callee); + + // Update known progress support for this playback technology + this.support.timeupdateEvent = true; + + // Turn off manual progress tracking + this.player.manualTimeUpdatesOff(); + }); + }, + + manualTimeUpdatesOff: function(){ + this.manualTimeUpdates = false; + this.stopTrackingCurrentTime(); + this.removeEvent("play", this.trackCurrentTime); + this.removeEvent("pause", this.stopTrackingCurrentTime); + }, + + trackCurrentTime: function(){ + if (this.currentTimeInterval) { this.stopTrackingCurrentTime(); } + this.currentTimeInterval = setInterval(_V_.proxy(this, function(){ + this.triggerEvent("timeupdate"); + }), 250); // 42 = 24 fps // 250 is what Webkit uses // FF uses 15 + }, + + // Turn off play progress tracking (when paused or dragging) + stopTrackingCurrentTime: function(){ clearInterval(this.currentTimeInterval); }, + + /* Player event handlers (how the player reacts to certain events) + ================================================================================ */ + onEnded: function(){ + if (this.options.loop) { + this.currentTime(0); + this.play(); + } else { + this.pause(); + this.currentTime(0); + this.pause(); + } + }, + + onPlay: function(){ + _V_.removeClass(this.el, "vjs-paused"); + _V_.addClass(this.el, "vjs-playing"); + }, + + onPause: function(){ + _V_.removeClass(this.el, "vjs-playing"); + _V_.addClass(this.el, "vjs-paused"); + }, + + onError: function(e) { + _V_.log("Video Error", e); + }, + +/* Player API +================================================================================ */ + + apiCall: function(method, arg){ + if (this.isReady) { + return this.tech[method](arg); + } else { + _V_.log("The playback technology API is not ready yet. Use player.ready(myFunction)."+" ["+method+"]", arguments.callee.caller.arguments.callee.caller.arguments.callee.caller) + return false; + // throw new Error("The playback technology API is not ready yet. Use player.ready(myFunction)."+" ["+method+"]"); + } + }, + + play: function(){ + this.apiCall("play"); return this; + }, + pause: function(){ + this.apiCall("pause"); return this; + }, + paused: function(){ + return this.apiCall("paused"); + }, + + currentTime: function(seconds){ + if (seconds !== undefined) { + + // Cache the last set value for smoother scrubbing. + this.values.lastSetCurrentTime = seconds; + + this.apiCall("setCurrentTime", seconds); + + if (this.manualTimeUpdates) { + this.triggerEvent("timeupdate"); + } + return this; + } + + // Cache last currentTime and return + return this.values.currentTime = this.apiCall("currentTime"); + }, + duration: function(){ + return this.apiCall("duration"); + }, + remainingTime: function(){ + return this.duration() - this.currentTime(); + }, + + buffered: function(){ + var buffered = this.apiCall("buffered"), + start = 0, end = this.values.bufferEnd = this.values.bufferEnd || 0, + timeRange; + + if (buffered && buffered.length > 0 && buffered.end(0) !== end) { + end = buffered.end(0); + // Storing values allows them be overridden by setBufferedFromProgress + this.values.bufferEnd = end; + } + + return _V_.createTimeRange(start, end); + }, + + // Calculates amount of buffer is full + bufferedPercent: function(){ + return (this.duration()) ? this.buffered().end(0) / this.duration() : 0; + }, + + volume: function(percentAsDecimal){ + if (percentAsDecimal !== undefined) { + var vol = Math.max(0, Math.min(1, parseFloat(percentAsDecimal))); // Force value to between 0 and 1 + this.values.volume = vol; + this.apiCall("setVolume", vol); + _V_.setLocalStorage("volume", vol); + return this; + } + // if (this.values.volume) { return this.values.volume; } + return this.apiCall("volume"); + }, + muted: function(muted){ + if (muted !== undefined) { + this.apiCall("setMuted", muted); + return this; + } + return this.apiCall("muted"); + }, + + width: function(width, skipListeners){ + if (width !== undefined) { + this.el.width = width; + this.el.style.width = width+"px"; + if (!skipListeners) { this.triggerEvent("resize"); } + return this; + } + return parseInt(this.el.getAttribute("width")); + }, + height: function(height){ + if (height !== undefined) { + this.el.height = height; + this.el.style.height = height+"px"; + this.triggerEvent("resize"); + return this; + } + return parseInt(this.el.getAttribute("height")); + }, + size: function(width, height){ + // Skip resize listeners on width for optimization + return this.width(width, true).height(height); + }, + + supportsFullScreen: function(){ return this.apiCall("supportsFullScreen"); }, + + // Turn on fullscreen (or window) mode + requestFullScreen: function(){ + var requestFullScreen = _V_.support.requestFullScreen; + + this.isFullScreen = true; + + // Check for browser element fullscreen support + if (requestFullScreen) { + + // Flash and other plugins get reloaded when you take their parent to fullscreen. + // To fix that we'll remove the tech, and reload it after the resize has finished. + if (this.tech.support.fullscreenResize === false && this.options.flash.iFrameMode != true) { + + this.pause(); + this.unloadTech(); + + _V_.addEvent(document, requestFullScreen.eventName, this.proxy(function(){ + _V_.removeEvent(document, requestFullScreen.eventName, arguments.callee); + this.loadTech(this.techName, { src: this.values.src }); + })); + + this.el[requestFullScreen.requestFn](); + + } else { + this.el[requestFullScreen.requestFn](); + } + + // In case the user presses escape to exit fullscreen, we need to update fullscreen status + _V_.addEvent(document, requestFullScreen.eventName, this.proxy(function(){ + this.isFullScreen = document[requestFullScreen.isFullScreen]; + })); + + } else if (this.tech.supportsFullScreen()) { + this.apiCall("enterFullScreen"); + + } else { + this.enterFullWindow(); + } + + this.triggerEvent("fullscreenchange"); + + return this; + }, + + cancelFullScreen: function(){ + var requestFullScreen = _V_.support.requestFullScreen; + + // Check for browser element fullscreen support + if (requestFullScreen) { + + // Flash and other plugins get reloaded when you take their parent to fullscreen. + // To fix that we'll remove the tech, and reload it after the resize has finished. + if (this.tech.support.fullscreenResize === false && this.options.flash.iFrameMode != true) { + + this.pause(); + this.unloadTech(); + + _V_.addEvent(document, requestFullScreen.eventName, this.proxy(function(){ + _V_.removeEvent(document, requestFullScreen.eventName, arguments.callee); + this.loadTech(this.techName, { src: this.values.src }) + })); + + document[requestFullScreen.cancelFn](); + + } else { + document[requestFullScreen.cancelFn](); + } + + } else if (this.tech.supportsFullScreen()) { + this.apiCall("exitFullScreen"); + + } else { + this.exitFullWindow(); + } + + this.isFullScreen = false; + this.triggerEvent("fullscreenchange"); + + return this; + }, + + enterFullWindow: function(){ + this.isFullWindow = true; + + // Storing original doc overflow value to return to when fullscreen is off + this.docOrigOverflow = document.documentElement.style.overflow; + + // Add listener for esc key to exit fullscreen + _V_.addEvent(document, "keydown", _V_.proxy(this, this.fullWindowOnEscKey)); + + // Hide any scroll bars + document.documentElement.style.overflow = 'hidden'; + + // Apply fullscreen styles + _V_.addClass(document.body, "vjs-full-window"); + _V_.addClass(this.el, "vjs-fullscreen"); + + this.triggerEvent("enterFullWindow"); + }, + + fullWindowOnEscKey: function(event){ + if (event.keyCode == 27) { + if (this.isFullScreen == true) { + this.cancelFullScreen(); + } else { + this.exitFullWindow(); + } + } + }, + + exitFullWindow: function(){ + this.isFullWindow = false; + _V_.removeEvent(document, "keydown", this.fullWindowOnEscKey); + + // Unhide scroll bars. + document.documentElement.style.overflow = this.docOrigOverflow; + + // Remove fullscreen styles + _V_.removeClass(document.body, "vjs-full-window"); + _V_.removeClass(this.el, "vjs-fullscreen"); + + // Resize the box, controller, and poster to original sizes + // this.positionAll(); + this.triggerEvent("exitFullWindow"); + }, + + // src is a pretty powerful function + // If you pass it an array of source objects, it will find the best source to play and use that object.src + // If the new source requires a new playback technology, it will switch to that. + // If you pass it an object, it will set the source to object.src + // If you pass it anything else (url string) it will set the video source to that + src: function(source){ + // Case: Array of source objects to choose from and pick the best to play + if (source instanceof Array) { + + var sources = source; + + techLoop: // Named loop for breaking both loops + // Loop through each playback technology in the options order + for (var i=0,j=this.options.techOrder;i<j.length;i++) { + var techName = j[i], + tech = _V_[techName]; + // tech = _V_.tech[techName]; + + // Check if the browser supports this technology + if (tech.isSupported()) { + + // Loop through each source object + for (var a=0,b=sources;a<b.length;a++) { + var source = b[a]; + + // Check if source can be played with this technology + if (tech.canPlaySource.call(this, source)) { + + // If this technology is already loaded, set source + if (techName == this.techName) { + this.src(source); // Passing the source object + + // Otherwise load this technology with chosen source + } else { + this.loadTech(techName, source); + } + + break techLoop; // Break both loops + } + } + } + } + + // Case: Source object { src: "", type: "" ... } + } else if (source instanceof Object) { + if (_V_[this.techName].canPlaySource(source)) { + this.src(source.src); + } else { + // Send through tech loop to check for a compatible technology. + this.src([source]); + } + // Case: URL String (http://myvideo...) + } else { + // Cache for getting last set source + this.values.src = source; + + if (!this.isReady) { + this.ready(function(){ + this.src(source); + }); + } else { + this.apiCall("src", source); + if (this.options.preload == "auto") { + this.load(); + } + if (this.options.autoplay) { + this.play(); + } + } + } + return this; + }, + + // Begin loading the src data + load: function(){ + this.apiCall("load"); + return this; + }, + currentSrc: function(){ + return this.apiCall("currentSrc"); + }, + + textTrackValue: function(kind, value){ + if (value !== undefined) { + this.values[kind] = value; + this.triggerEvent(kind+"update"); + return this; + } + return this.values[kind]; + }, + + // Attributes/Options + preload: function(value){ + if (value !== undefined) { + this.apiCall("setPreload", value); + this.options.preload = value; + return this; + } + return this.apiCall("preload", value); + }, + autoplay: function(value){ + if (value !== undefined) { + this.apiCall("setAutoplay", value); + this.options.autoplay = value; + return this; + } + return this.apiCall("autoplay", value); + }, + loop: function(value){ + if (value !== undefined) { + this.apiCall("setLoop", value); + this.options.loop = value; + return this; + } + return this.apiCall("loop", value); + }, + + controls: function(){ return this.options.controls; }, + textTracks: function(){ return this.options.tracks; }, + poster: function(){ return this.apiCall("poster"); }, + + error: function(){ return this.apiCall("error"); }, + networkState: function(){ return this.apiCall("networkState"); }, + readyState: function(){ return this.apiCall("readyState"); }, + seeking: function(){ return this.apiCall("seeking"); }, + initialTime: function(){ return this.apiCall("initialTime"); }, + startOffsetTime: function(){ return this.apiCall("startOffsetTime"); }, + played: function(){ return this.apiCall("played"); }, + seekable: function(){ return this.apiCall("seekable"); }, + ended: function(){ return this.apiCall("ended"); }, + videoTracks: function(){ return this.apiCall("videoTracks"); }, + audioTracks: function(){ return this.apiCall("audioTracks"); }, + videoWidth: function(){ return this.apiCall("videoWidth"); }, + videoHeight: function(){ return this.apiCall("videoHeight"); }, + defaultPlaybackRate: function(){ return this.apiCall("defaultPlaybackRate"); }, + playbackRate: function(){ return this.apiCall("playbackRate"); }, + // mediaGroup: function(){ return this.apiCall("mediaGroup"); }, + // controller: function(){ return this.apiCall("controller"); }, + controls: function(){ return this.apiCall("controls"); }, + defaultMuted: function(){ return this.apiCall("defaultMuted"); } +}); + +// RequestFullscreen API +(function(){ + var requestFn, + cancelFn, + eventName, + isFullScreen, + playerProto = _V_.Player.prototype; + + // Current W3C Spec + // http://dvcs.w3.org/hg/fullscreen/raw-file/tip/Overview.html#api + // Mozilla Draft: https://wiki.mozilla.org/Gecko:FullScreenAPI#fullscreenchange_event + if (document.cancelFullscreen !== undefined) { + requestFn = "requestFullscreen"; + cancelFn = "exitFullscreen"; + eventName = "fullscreenchange"; + isFullScreen = "fullScreen"; + + // Webkit (Chrome/Safari) and Mozilla (Firefox) have working implementaitons + // that use prefixes and vary slightly from the new W3C spec. Specifically, using 'exit' instead of 'cancel', + // and lowercasing the 'S' in Fullscreen. + // Other browsers don't have any hints of which version they might follow yet, so not going to try to predict by loopeing through all prefixes. + } else { + + _V_.each(["moz", "webkit"], function(prefix){ + + // https://github.com/zencoder/video-js/pull/128 + if ((prefix != "moz" || document.mozFullScreenEnabled) && document[prefix + "CancelFullScreen"] !== undefined) { + requestFn = prefix + "RequestFullScreen"; + cancelFn = prefix + "CancelFullScreen"; + eventName = prefix + "fullscreenchange"; + + if (prefix == "webkit") { + isFullScreen = prefix + "IsFullScreen"; + } else { + _V_.log("moz here") + isFullScreen = prefix + "FullScreen"; + } + } + + }); + + } + + if (requestFn) { + _V_.support.requestFullScreen = { + requestFn: requestFn, + cancelFn: cancelFn, + eventName: eventName, + isFullScreen: isFullScreen + }; + } + +})();/* Playback Technology - Base class for playback technologies +================================================================================ */ +_V_.PlaybackTech = _V_.Component.extend({ + init: function(player, options){ + // this._super(player, options); + + // Make playback element clickable + // _V_.addEvent(this.el, "click", _V_.proxy(this, _V_.PlayToggle.prototype.onClick)); + + // this.addEvent("click", this.proxy(this.onClick)); + + // player.triggerEvent("techready"); + }, + // destroy: function(){}, + // createElement: function(){}, + onClick: function(){ + if (this.player.options.controls) { + _V_.PlayToggle.prototype.onClick.call(this); + } + } +}); + +// Create placeholder methods for each that warn when a method isn't supported by the current playback technology +_V_.apiMethods = "play,pause,paused,currentTime,setCurrentTime,duration,buffered,volume,setVolume,muted,setMuted,width,height,supportsFullScreen,enterFullScreen,src,load,currentSrc,preload,setPreload,autoplay,setAutoplay,loop,setLoop,error,networkState,readyState,seeking,initialTime,startOffsetTime,played,seekable,ended,videoTracks,audioTracks,videoWidth,videoHeight,textTracks,defaultPlaybackRate,playbackRate,mediaGroup,controller,controls,defaultMuted".split(","); +_V_.each(_V_.apiMethods, function(methodName){ + _V_.PlaybackTech.prototype[methodName] = function(){ + throw new Error("The '"+method+"' method is not available on the playback technology's API"); + } +}); + +/* HTML5 Playback Technology - Wrapper for HTML5 Media API +================================================================================ */ +_V_.html5 = _V_.PlaybackTech.extend({ + + init: function(player, options, ready){ + this.player = player; + this.el = this.createElement(); + this.ready(ready); + + this.addEvent("click", this.proxy(this.onClick)); + + var source = options.source; + + // If the element source is already set, we may have missed the loadstart event, and want to trigger it. + // We don't want to set the source again and interrupt playback. + if (source && this.el.currentSrc == source.src) { + player.triggerEvent("loadstart"); + + // Otherwise set the source if one was provided. + } else if (source) { + this.el.src = source.src; + } + + // Chrome and Safari both have issues with autoplay. + // In Safari (5.1.1), when we move the video element into the container div, autoplay doesn't work. + // In Chrome (15), if you have autoplay + a poster + no controls, the video gets hidden (but audio plays) + // This fixes both issues. Need to wait for API, so it updates displays correctly + player.ready(function(){ + if (this.options.autoplay && this.paused()) { + this.tag.poster = null; // Chrome Fix. Fixed in Chrome v16. + this.play(); + } + }); + + this.setupTriggers(); + + this.triggerReady(); + }, + + destroy: function(){ + this.player.tag = false; + this.removeTriggers(); + this.el.parentNode.removeChild(this.el); + }, + + createElement: function(){ + var html5 = _V_.html5, + player = this.player, + + // If possible, reuse original tag for HTML5 playback technology element + el = player.tag, + newEl; + + // Check if this browser supports moving the element into the box. + // On the iPhone video will break if you move the element, + // So we have to create a brand new element. + if (!el || this.support.movingElementInDOM === false) { + + // If the original tag is still there, remove it. + if (el) { + player.el.removeChild(el); + } + + newEl = _V_.createElement("video", { + id: el.id || player.el.id + "_html5_api", + className: el.className || "vjs-tech" + }); + + el = newEl; + _V_.insertFirst(el, player.el); + } + + // Update tag settings, in case they were overridden + _V_.each(["autoplay","preload","loop","muted"], function(attr){ // ,"poster" + el[attr] = player.options[attr]; + }, this); + + return el; + }, + + // Make video events trigger player events + // May seem verbose here, but makes other APIs possible. + setupTriggers: function(){ + _V_.each.call(this, _V_.html5.events, function(type){ + _V_.addEvent(this.el, type, _V_.proxy(this.player, this.eventHandler)); + }); + }, + removeTriggers: function(){ + _V_.each.call(this, _V_.html5.events, function(type){ + _V_.removeEvent(this.el, type, _V_.proxy(this.player, this.eventHandler)); + }); + }, + eventHandler: function(e){ + e.stopPropagation(); + this.triggerEvent(e); + }, + + play: function(){ this.el.play(); }, + pause: function(){ this.el.pause(); }, + paused: function(){ return this.el.paused; }, + + currentTime: function(){ return this.el.currentTime; }, + setCurrentTime: function(seconds){ + try { + this.el.currentTime = seconds; + } catch(e) { + _V_.log(e, "Video isn't ready. (VideoJS)"); + // this.warning(VideoJS.warnings.videoNotReady); + } + }, + + duration: function(){ return this.el.duration || 0; }, + buffered: function(){ return this.el.buffered; }, + + volume: function(){ return this.el.volume; }, + setVolume: function(percentAsDecimal){ this.el.volume = percentAsDecimal; }, + muted: function(){ return this.el.muted; }, + setMuted: function(muted){ this.el.muted = muted }, + + width: function(){ return this.el.offsetWidth; }, + height: function(){ return this.el.offsetHeight; }, + + supportsFullScreen: function(){ + if (typeof this.el.webkitEnterFullScreen == 'function') { + + // Seems to be broken in Chromium/Chrome && Safari in Leopard + if (!navigator.userAgent.match("Chrome") && !navigator.userAgent.match("Mac OS X 10.5")) { + return true; + } + } + return false; + }, + + enterFullScreen: function(){ + try { + this.el.webkitEnterFullScreen(); + } catch (e) { + if (e.code == 11) { + // this.warning(VideoJS.warnings.videoNotReady); + _V_.log("VideoJS: Video not ready.") + } + } + }, + src: function(src){ this.el.src = src; }, + load: function(){ this.el.load(); }, + currentSrc: function(){ return this.el.currentSrc; }, + + preload: function(){ return this.el.preload; }, + setPreload: function(val){ this.el.preload = val; }, + autoplay: function(){ return this.el.autoplay; }, + setAutoplay: function(val){ this.el.autoplay = val; }, + loop: function(){ return this.el.loop; }, + setLoop: function(val){ this.el.loop = val; }, + + error: function(){ return this.el.error; }, + // networkState: function(){ return this.el.networkState; }, + // readyState: function(){ return this.el.readyState; }, + seeking: function(){ return this.el.seeking; }, + // initialTime: function(){ return this.el.initialTime; }, + // startOffsetTime: function(){ return this.el.startOffsetTime; }, + // played: function(){ return this.el.played; }, + // seekable: function(){ return this.el.seekable; }, + ended: function(){ return this.el.ended; }, + // videoTracks: function(){ return this.el.videoTracks; }, + // audioTracks: function(){ return this.el.audioTracks; }, + // videoWidth: function(){ return this.el.videoWidth; }, + // videoHeight: function(){ return this.el.videoHeight; }, + // textTracks: function(){ return this.el.textTracks; }, + // defaultPlaybackRate: function(){ return this.el.defaultPlaybackRate; }, + // playbackRate: function(){ return this.el.playbackRate; }, + // mediaGroup: function(){ return this.el.mediaGroup; }, + // controller: function(){ return this.el.controller; }, + controls: function(){ return this.player.options.controls; }, + defaultMuted: function(){ return this.el.defaultMuted; } +}); + +/* HTML5 Support Testing -------------------------------------------------------- */ + +_V_.html5.isSupported = function(){ + return !!document.createElement("video").canPlayType; +}; + +_V_.html5.canPlaySource = function(srcObj){ + return !!document.createElement("video").canPlayType(srcObj.type); + // TODO: Check Type + // If no Type, check ext + // Check Media Type +}; + +// List of all HTML5 events (various uses). +_V_.html5.events = "loadstart,suspend,abort,error,emptied,stalled,loadedmetadata,loadeddata,canplay,canplaythrough,playing,waiting,seeking,seeked,ended,durationchange,timeupdate,progress,play,pause,ratechange,volumechange".split(","); + +/* HTML5 Device Fixes ---------------------------------------------------------- */ + +_V_.html5.prototype.support = { + + // Support for tech specific full screen. (webkitEnterFullScreen, not requestFullscreen) + // http://developer.apple.com/library/safari/#documentation/AudioVideo/Reference/HTMLVideoElementClassReference/HTMLVideoElement/HTMLVideoElement.html + // Seems to be broken in Chromium/Chrome && Safari in Leopard + fullscreen: (typeof _V_.testVid.webkitEnterFullScreen !== undefined) ? (!_V_.ua.match("Chrome") && !_V_.ua.match("Mac OS X 10.5") ? true : false) : false, + + // In iOS, if you move a video element in the DOM, it breaks video playback. + movingElementInDOM: !_V_.isIOS() + +}; + +// Android +if (_V_.isAndroid()) { + + // Override Android 2.2 and less canPlayType method which is broken + if (_V_.androidVersion() < 3) { + document.createElement("video").constructor.prototype.canPlayType = function(type){ + return (type && type.toLowerCase().indexOf("video/mp4") != -1) ? "maybe" : ""; + }; + } +} + + +/* VideoJS-SWF - Custom Flash Player with HTML5-ish API - https://github.com/zencoder/video-js-swf +================================================================================ */ +_V_.flash = _V_.PlaybackTech.extend({ + + init: function(player, options){ + this.player = player; + + var source = options.source, + + // Which element to embed in + parentEl = options.parentEl, + + // Create a temporary element to be replaced by swf object + placeHolder = this.el = _V_.createElement("div", { id: parentEl.id + "_temp_flash" }), + + // Generate ID for swf object + objId = player.el.id+"_flash_api", + + // Store player options in local var for optimization + playerOptions = player.options, + + // Merge default flashvars with ones passed in to init + flashVars = _V_.merge({ + + // SWF Callback Functions + readyFunction: "_V_.flash.onReady", + eventProxyFunction: "_V_.flash.onEvent", + errorEventProxyFunction: "_V_.flash.onError", + + // Player Settings + autoplay: playerOptions.autoplay, + preload: playerOptions.preload, + loop: playerOptions.loop, + muted: playerOptions.muted + + }, options.flashVars), + + // Merge default parames with ones passed in + params = _V_.merge({ + wmode: "opaque", // Opaque is needed to overlay controls, but can affect playback performance + bgcolor: "#000000" // Using bgcolor prevents a white flash when the object is loading + }, options.params), + + // Merge default attributes with ones passed in + attributes = _V_.merge({ + id: objId, + name: objId, // Both ID and Name needed or swf to identifty itself + 'class': 'vjs-tech' + }, options.attributes) + ; + + // If source was supplied pass as a flash var. + if (source) { + flashVars.src = encodeURIComponent(source.src); + } + + // Add placeholder to player div + _V_.insertFirst(placeHolder, parentEl); + + // Having issues with Flash reloading on certain page actions (hide/resize/fullscreen) in certain browsers + // This allows resetting the playhead when we catch the reload + if (options.startTime) { + this.ready(function(){ + this.load(); + this.play(); + this.currentTime(options.startTime); + }); + } + + // Flash iFrame Mode + // In web browsers there are multiple instances where changing the parent element or visibility of a plugin causes the plugin to reload. + // - Firefox just about always. https://bugzilla.mozilla.org/show_bug.cgi?id=90268 (might be fixed by version 13) + // - Webkit when hiding the plugin + // - Webkit and Firefox when using requestFullScreen on a parent element + // Loading the flash plugin into a dynamically generated iFrame gets around most of these issues. + // Issues that remain include hiding the element and requestFullScreen in Firefox specifically + + // There's on particularly annoying issue with this method which is that Firefox throws a security error on an offsite Flash object loaded into a dynamically created iFrame. + // Even though the iframe was inserted into a page on the web, Firefox + Flash considers it a local app trying to access an internet file. + // I tried mulitple ways of setting the iframe src attribute but couldn't find a src that worked well. Tried a real/fake source, in/out of domain. + // Also tried a method from stackoverflow that caused a security error in all browsers. http://stackoverflow.com/questions/2486901/how-to-set-document-domain-for-a-dynamically-generated-iframe + // In the end the solution I found to work was setting the iframe window.location.href right before doing a document.write of the Flash object. + // The only downside of this it seems to trigger another http request to the original page (no matter what's put in the href). Not sure why that is. + + // NOTE (2012-01-29): Cannot get Firefox to load the remote hosted SWF into a dynamically created iFrame + // Firefox 9 throws a security error, unleess you call location.href right before doc.write. + // Not sure why that even works, but it causes the browser to look like it's continuously trying to load the page. + // Firefox 3.6 keeps calling the iframe onload function anytime I write to it, causing an endless loop. + + if (options.iFrameMode == true && !_V_.isFF) { + + // Create iFrame with vjs-tech class so it's 100% width/height + var iFrm = _V_.createElement("iframe", { + id: objId + "_iframe", + name: objId + "_iframe", + className: "vjs-tech", + scrolling: "no", + marginWidth: 0, + marginHeight: 0, + frameBorder: 0 + }); + + // Update ready function names in flash vars for iframe window + flashVars.readyFunction = "ready"; + flashVars.eventProxyFunction = "events"; + flashVars.errorEventProxyFunction = "errors"; + + // Tried multiple methods to get this to work in all browsers + + // Tried embedding the flash object in the page first, and then adding a place holder to the iframe, then replacing the placeholder with the page object. + // The goal here was to try to load the swf URL in the parent page first and hope that got around the firefox security error + // var newObj = _V_.flash.embed(options.swf, placeHolder, flashVars, params, attributes); + // (in onload) + // var temp = _V_.createElement("a", { id:"asdf", innerHTML: "asdf" } ); + // iDoc.body.appendChild(temp); + + // Tried embedding the flash object through javascript in the iframe source. + // This works in webkit but still triggers the firefox security error + // iFrm.src = "javascript: document.write('"+_V_.flash.getEmbedCode(options.swf, flashVars, params, attributes)+"');"; + + // Tried an actual local iframe just to make sure that works, but it kills the easiness of the CDN version if you require the user to host an iframe + // We should add an option to host the iframe locally though, because it could help a lot of issues. + // iFrm.src = "iframe.html"; + + // Wait until iFrame has loaded to write into it. + _V_.addEvent(iFrm, "load", _V_.proxy(this, function(){ + + var iDoc, objTag, swfLoc, + iWin = iFrm.contentWindow, + varString = ""; + + + // The one working method I found was to use the iframe's document.write() to create the swf object + // This got around the security issue in all browsers except firefox. + // I did find a hack where if I call the iframe's window.location.href="", it would get around the security error + // However, the main page would look like it was loading indefinitely (URL bar loading spinner would never stop) + // Plus Firefox 3.6 didn't work no matter what I tried. + // if (_V_.ua.match("Firefox")) { + // iWin.location.href = ""; + // } + + // Get the iFrame's document depending on what the browser supports + iDoc = iFrm.contentDocument ? iFrm.contentDocument : iFrm.contentWindow.document; + + // Tried ensuring both document domains were the same, but they already were, so that wasn't the issue. + // Even tried adding /. that was mentioned in a browser security writeup + // document.domain = document.domain+"/."; + // iDoc.domain = document.domain+"/."; + + // Tried adding the object to the iframe doc's innerHTML. Security error in all browsers. + // iDoc.body.innerHTML = swfObjectHTML; + + // Tried appending the object to the iframe doc's body. Security error in all browsers. + // iDoc.body.appendChild(swfObject); + + // Using document.write actually got around the security error that browsers were throwing. + // Again, it's a dynamically generated (same domain) iframe, loading an external Flash swf. + // Not sure why that's a security issue, but apparently it is. + iDoc.write(_V_.flash.getEmbedCode(options.swf, flashVars, params, attributes)); + + // Setting variables on the window needs to come after the doc write because otherwise they can get reset in some browsers + // So far no issues with swf ready event being called before it's set on the window. + iWin.player = this.player; + + // Create swf ready function for iFrame window + iWin.ready = _V_.proxy(this.player, function(currSwf){ + var el = iDoc.getElementById(currSwf), + player = this, + tech = player.tech; + + // Update reference to playback technology element + tech.el = el; + + // Now that the element is ready, make a click on the swf play the video + _V_.addEvent(el, "click", tech.proxy(tech.onClick)); + + // Make sure swf is actually ready. Sometimes the API isn't actually yet. + _V_.flash.checkReady(tech); + }); + + // Create event listener for all swf events + iWin.events = _V_.proxy(this.player, function(swfID, eventName, other){ + var player = this; + if (player && player.techName == "flash") { + player.triggerEvent(eventName); + } + }); + + // Create error listener for all swf errors + iWin.errors = _V_.proxy(this.player, function(swfID, eventName){ + _V_.log("Flash Error", eventName); + }); + + })); + + // Replace placeholder with iFrame (it will load now) + placeHolder.parentNode.replaceChild(iFrm, placeHolder); + + // If not using iFrame mode, embed as normal object + } else { + _V_.flash.embed(options.swf, placeHolder, flashVars, params, attributes); + } + }, + + destroy: function(){ + this.el.parentNode.removeChild(this.el); + }, + + // setupTriggers: function(){}, // Using global onEvent func to distribute events + + play: function(){ this.el.vjs_play(); }, + pause: function(){ this.el.vjs_pause(); }, + src: function(src){ + this.el.vjs_src(src); + + // Currently the SWF doesn't autoplay if you load a source later. + // e.g. Load player w/ no source, wait 2s, set src. + if (this.player.autoplay) { + var tech = this; + setTimeout(function(){ tech.play(); }, 0); + } + }, + load: function(){ this.el.vjs_load(); }, + poster: function(){ this.el.vjs_getProperty("poster"); }, + + buffered: function(){ + return _V_.createTimeRange(0, this.el.vjs_getProperty("buffered")); + }, + + supportsFullScreen: function(){ + return false; // Flash does not allow fullscreen through javascript + }, + enterFullScreen: function(){ + return false; + } +}); + +// Create setters and getters for attributes +(function(){ + + var api = _V_.flash.prototype, + readWrite = "preload,currentTime,defaultPlaybackRate,playbackRate,autoplay,loop,mediaGroup,controller,controls,volume,muted,defaultMuted".split(","), + readOnly = "error,currentSrc,networkState,readyState,seeking,initialTime,duration,startOffsetTime,paused,played,seekable,ended,videoTracks,audioTracks,videoWidth,videoHeight,textTracks".split(","), + callOnly = "load,play,pause".split(","); + // Overridden: buffered + + createSetter = function(attr){ + var attrUpper = attr.charAt(0).toUpperCase() + attr.slice(1); + api["set"+attrUpper] = function(val){ return this.el.vjs_setProperty(attr, val); }; + }, + + createGetter = function(attr){ + api[attr] = function(){ return this.el.vjs_getProperty(attr); }; + } + ; + + // Create getter and setters for all read/write attributes + _V_.each(readWrite, function(attr){ + createGetter(attr); + createSetter(attr); + }); + + // Create getters for read-only attributes + _V_.each(readOnly, function(attr){ + createGetter(attr); + }); + +})(); + +/* Flash Support Testing -------------------------------------------------------- */ + +_V_.flash.isSupported = function(){ + return _V_.flash.version()[0] >= 10; + // return swfobject.hasFlashPlayerVersion("10"); +}; + +_V_.flash.canPlaySource = function(srcObj){ + if (srcObj.type in _V_.flash.prototype.support.formats) { return "maybe"; } +}; + +_V_.flash.prototype.support = { + formats: { + "video/flv": "FLV", + "video/x-flv": "FLV", + "video/mp4": "MP4", + "video/m4v": "MP4" + }, + + // Optional events that we can manually mimic with timers + progressEvent: false, + timeupdateEvent: false, + + // Resizing plugins using request fullscreen reloads the plugin + fullscreenResize: false, + + // Resizing plugins in Firefox always reloads the plugin (e.g. full window mode) + parentResize: !(_V_.ua.match("Firefox")) +}; + +_V_.flash.onReady = function(currSwf){ + + var el = _V_.el(currSwf); + + // Get player from box + // On firefox reloads, el might already have a player + var player = el.player || el.parentNode.player, + tech = player.tech; + + // Reference player on tech element + el.player = player; + + // Update reference to playback technology element + tech.el = el; + + // Now that the element is ready, make a click on the swf play the video + tech.addEvent("click", tech.onClick); + + _V_.flash.checkReady(tech); +}; + +// The SWF isn't alwasy ready when it says it is. Sometimes the API functions still need to be added to the object. +// If it's not ready, we set a timeout to check again shortly. +_V_.flash.checkReady = function(tech){ + + // Check if API property exists + if (tech.el.vjs_getProperty) { + + // If so, tell tech it's ready + tech.triggerReady(); + + // Otherwise wait longer. + } else { + + setTimeout(function(){ + _V_.flash.checkReady(tech); + }, 50); + + } +}; + +// Trigger events from the swf on the player +_V_.flash.onEvent = function(swfID, eventName){ + var player = _V_.el(swfID).player; + player.triggerEvent(eventName); +}; + +// Log errors from the swf +_V_.flash.onError = function(swfID, err){ + _V_.log("Flash Error", err, swfID); +}; + +// Flash Version Check +_V_.flash.version = function(){ + var version = '0,0,0' + + // IE + try { + version = new ActiveXObject('ShockwaveFlash.ShockwaveFlash').GetVariable('$version').replace(/\D+/g, ',').match(/^,?(.+),?$/)[1]; + + // other browsers + } catch(e) { + try { + if (navigator.mimeTypes["application/x-shockwave-flash"].enabledPlugin){ + version = (navigator.plugins["Shockwave Flash 2.0"] || navigator.plugins["Shockwave Flash"]).description.replace(/\D+/g, ",").match(/^,?(.+),?$/)[1]; + } + } catch(e) {} + } + return version.split(","); +} + +// Flash embedding method. Only used in non-iframe mode +_V_.flash.embed = function(swf, placeHolder, flashVars, params, attributes){ + var code = _V_.flash.getEmbedCode(swf, flashVars, params, attributes), + + // Get element by embedding code and retrieving created element + obj = _V_.createElement("div", { innerHTML: code }).childNodes[0], + + par = placeHolder.parentNode + ; + + placeHolder.parentNode.replaceChild(obj, placeHolder); + + // IE6 seems to have an issue where it won't initialize the swf object after injecting it. + // This is a dumb temporary fix + if (_V_.isIE()) { + var newObj = par.childNodes[0]; + setTimeout(function(){ + newObj.style.display = "block"; + }, 1000); + } + + return obj; + +}; + +_V_.flash.getEmbedCode = function(swf, flashVars, params, attributes){ + + var objTag = '<object type="application/x-shockwave-flash"', + flashVarsString = '', + paramsString = '' + attrsString = ''; + + // Convert flash vars to string + if (flashVars) { + _V_.eachProp(flashVars, function(key, val){ + flashVarsString += (key + "=" + val + "&"); + }); + } + + // Add swf, flashVars, and other default params + params = _V_.merge({ + movie: swf, + flashvars: flashVarsString, + allowScriptAccess: "always", // Required to talk to swf + allowNetworking: "all" // All should be default, but having security issues. + }, params); + + // Create param tags string + _V_.eachProp(params, function(key, val){ + paramsString += '<param name="'+key+'" value="'+val+'" />'; + }); + + attributes = _V_.merge({ + // Add swf to attributes (need both for IE and Others to work) + data: swf, + + // Default to 100% width/height + width: "100%", + height: "100%" + + }, attributes); + + // Create Attributes string + _V_.eachProp(attributes, function(key, val){ + attrsString += (key + '="' + val + '" '); + }); + + return objTag + attrsString + '>' + paramsString + '</object>'; +} +_V_.Track = function(attributes, player){ + // Store reference to the parent player + this.player = player; + + this.src = attributes.src; + this.kind = attributes.kind; + this.srclang = attributes.srclang; + this.label = attributes.label; + this["default"] = attributes["default"]; // 'default' is reserved-ish + this.title = attributes.title; + + this.cues = []; + this.currentCue = false; + this.lastCueIndex = 0; + + // Update current cue on timeupdate + player.addEvent("timeupdate", _V_.proxy(this, this.update)); + + // Reset cue time on media end + player.addEvent("ended", _V_.proxy(this, function() { this.lastCueIndex = 0; })); + + // Load Track File + _V_.get(attributes.src, _V_.proxy(this, this.parseCues)); +}; + +_V_.Track.prototype = { + + parseCues: function(srcContent) { + var cue, time, text, + lines = srcContent.split("\n"), + line = ""; + + for (var i=0; i<lines.length; i++) { + line = _V_.trim(lines[i]); // Trim whitespace and linebreaks + if (line) { // Loop until a line with content + + // First line - Number + cue = { + id: line, // Cue Number + index: this.cues.length // Position in Array + }; + + // Second line - Time + line = _V_.trim(lines[++i]); + time = line.split(" --> "); + cue.startTime = this.parseCueTime(time[0]); + cue.endTime = this.parseCueTime(time[1]); + + // Additional lines - Cue Text + text = []; + for (var j=i; j<lines.length; j++) { // Loop until a blank line or end of lines + line = _V_.trim(lines[++i]); + if (!line) { break; } + text.push(line); + } + cue.text = text.join('<br/>'); + + // Add this cue + this.cues.push(cue); + } + } + }, + + parseCueTime: function(timeText) { + var parts = timeText.split(':'), + time = 0; + // hours => seconds + time += parseFloat(parts[0])*60*60; + // minutes => seconds + time += parseFloat(parts[1])*60; + // get seconds + var seconds = parts[2].split(/\.|,/); // Either . or , + time += parseFloat(seconds[0]); + // add miliseconds + ms = parseFloat(seconds[1]); + if (ms) { time += ms/1000; } + return time; + }, + + update: function(){ + // Assuming all cues are in order by time, and do not overlap + if (this.cues && this.cues.length > 0) { + var time = this.player.currentTime(); + // If current cue should stay showing, don't do anything. Otherwise, find new cue. + if (!this.currentCue || this.currentCue.startTime >= time || this.currentCue.endTime < time) { + var newSubIndex = false, + // Loop in reverse if lastCue is after current time (optimization) + // Meaning the user is scrubbing in reverse or rewinding + reverse = (this.cues[this.lastCueIndex].startTime > time), + // If reverse, step back 1 becase we know it's not the lastCue + i = this.lastCueIndex - (reverse ? 1 : 0); + while (true) { // Loop until broken + if (reverse) { // Looping in reverse + // Stop if no more, or this cue ends before the current time (no earlier cues should apply) + if (i < 0 || this.cues[i].endTime < time) { break; } + // End is greater than time, so if start is less, show this cue + if (this.cues[i].startTime < time) { + newSubIndex = i; + break; + } + i--; + } else { // Looping forward + // Stop if no more, or this cue starts after time (no later cues should apply) + if (i >= this.cues.length || this.cues[i].startTime > time) { break; } + // Start is less than time, so if end is later, show this cue + if (this.cues[i].endTime > time) { + newSubIndex = i; + break; + } + i++; + } + } + + // Set or clear current cue + if (newSubIndex !== false) { + this.currentCue = this.cues[newSubIndex]; + this.lastCueIndex = newSubIndex; + this.updatePlayer(this.currentCue.text); + } else if (this.currentCue) { + this.currentCue = false; + this.updatePlayer(""); + } + } + } + }, + + // Update the stored value for the current track kind + // and trigger an event to update all text track displays. + updatePlayer: function(text){ + this.player.textTrackValue(this.kind, text); + } +}; +_V_.addEvent(window, "load", function(){ + _V_.windowLoaded = true; +}); + +// Run Auto-load players +_V_.autoSetup(); +// Expose to global +window.VideoJS = window._V_ = VideoJS; + +// End self-executing function +})(window); diff --git a/extlib/video-js/video.min.js b/extlib/video-js/video.min.js new file mode 100644 index 00000000..026a0126 --- /dev/null +++ b/extlib/video-js/video.min.js @@ -0,0 +1,21 @@ +/*! +Video.js - HTML5 Video Player +Version 3.1.0 + +LGPL v3 LICENSE INFO +This file is part of Video.js. Copyright 2011 Zencoder, Inc. + +Video.js is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Video.js is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with Video.js. If not, see <http://www.gnu.org/licenses/>. +*/ +(function(window,undefined){var document=window.document;document.createElement("video");document.createElement("audio");var VideoJS=function(id,addOptions,ready){var tag;if(typeof id=="string"){if(id.indexOf("#")===0){id=id.slice(1)}if(_V_.players[id]){return _V_.players[id]}else{tag=_V_.el(id)}}else{tag=id}if(!tag||!tag.nodeName){throw new TypeError("The element or ID supplied is not valid. (VideoJS)")}return tag.player||new _V_.Player(tag,addOptions,ready)},_V_=VideoJS,CDN_VERSION="3.1";VideoJS.players={};VideoJS.options={techOrder:["html5","flash"],html5:{},flash:{swf:"http://vjs.zencdn.net/c/video-js.swf"},width:"auto",height:"auto",defaultVolume:0,components:{poster:{},loadingSpinner:{},bigPlayButton:{},controlBar:{},subtitlesDisplay:{}}};if(CDN_VERSION!="GENERATED_CDN_VSN"){_V_.options.flash.swf="http://vjs.zencdn.net/"+CDN_VERSION+"/video-js.swf"}_V_.autoSetup=function(){var options,vid,player,vids=document.getElementsByTagName("video");if(vids&&vids.length>0){for(var i=0,j=vids.length;i<j;i++){vid=vids[i];if(vid&&vid.getAttribute){if(vid.player===undefined){options=vid.getAttribute("data-setup");if(options!==null){options=JSON.parse(options||"{}");player=_V_(vid,options)}}}else{_V_.autoSetupTimeout(1);break}}}else{if(!_V_.windowLoaded){_V_.autoSetupTimeout(1)}}};_V_.autoSetupTimeout=function(wait){setTimeout(_V_.autoSetup,wait)};_V_.merge=function(obj1,obj2,safe){if(!obj2){obj2={}}for(var attrname in obj2){if(obj2.hasOwnProperty(attrname)&&(!safe||!obj1.hasOwnProperty(attrname))){obj1[attrname]=obj2[attrname]}}return obj1};_V_.extend=function(obj){this.merge(this,obj,true)};_V_.extend({tech:{},controlSets:{},isIE:function(){return !+"\v1"},isFF:function(){return !!_V_.ua.match("Firefox")},isIPad:function(){return navigator.userAgent.match(/iPad/i)!==null},isIPhone:function(){return navigator.userAgent.match(/iPhone/i)!==null},isIOS:function(){return VideoJS.isIPhone()||VideoJS.isIPad()},iOSVersion:function(){var match=navigator.userAgent.match(/OS (\d+)_/i);if(match&&match[1]){return match[1]}},isAndroid:function(){return navigator.userAgent.match(/Android.*AppleWebKit/i)!==null},androidVersion:function(){var match=navigator.userAgent.match(/Android (\d+)\./i);if(match&&match[1]){return match[1]}},testVid:document.createElement("video"),ua:navigator.userAgent,support:{},each:function(arr,fn){if(!arr||arr.length===0){return}for(var i=0,j=arr.length;i<j;i++){fn.call(this,arr[i],i)}},eachProp:function(obj,fn){if(!obj){return}for(var name in obj){if(obj.hasOwnProperty(name)){fn.call(this,name,obj[name])}}},el:function(id){return document.getElementById(id)},createElement:function(tagName,attributes){var el=document.createElement(tagName),attrname;for(attrname in attributes){if(attributes.hasOwnProperty(attrname)){if(attrname.indexOf("-")!==-1){el.setAttribute(attrname,attributes[attrname])}else{el[attrname]=attributes[attrname]}}}return el},insertFirst:function(node,parent){if(parent.firstChild){parent.insertBefore(node,parent.firstChild)}else{parent.appendChild(node)}},addClass:function(element,classToAdd){if((" "+element.className+" ").indexOf(" "+classToAdd+" ")==-1){element.className=element.className===""?classToAdd:element.className+" "+classToAdd}},removeClass:function(element,classToRemove){if(element.className.indexOf(classToRemove)==-1){return}var classNames=element.className.split(" ");classNames.splice(classNames.indexOf(classToRemove),1);element.className=classNames.join(" ")},remove:function(item,array){if(!array){return}var i=array.indexOf(item);if(i!=-1){return array.splice(i,1)}},blockTextSelection:function(){document.body.focus();document.onselectstart=function(){return false}},unblockTextSelection:function(){document.onselectstart=function(){return true}},formatTime:function(seconds,guide){guide=guide||seconds;var s=Math.floor(seconds%60),m=Math.floor(seconds/60%60),h=Math.floor(seconds/3600),gm=Math.floor(guide/60%60),gh=Math.floor(guide/3600);h=(h>0||gh>0)?h+":":"";m=(((h||gm>=10)&&m<10)?"0"+m:m)+":";s=(s<10)?"0"+s:s;return h+m+s},capitalize:function(string){return string.charAt(0).toUpperCase()+string.slice(1)},getRelativePosition:function(x,relativeElement){return Math.max(0,Math.min(1,(x-_V_.findPosX(relativeElement))/relativeElement.offsetWidth))},getComputedStyleValue:function(element,style){return window.getComputedStyle(element,null).getPropertyValue(style)},trim:function(string){return string.toString().replace(/^\s+/,"").replace(/\s+$/,"")},round:function(num,dec){if(!dec){dec=0}return Math.round(num*Math.pow(10,dec))/Math.pow(10,dec)},isEmpty:function(object){for(var prop in object){return false}return true},createTimeRange:function(start,end){return{length:1,start:function(){return start},end:function(){return end}}},cache:{},guid:1,expando:"vdata"+(new Date).getTime(),getData:function(elem){var id=elem[_V_.expando];if(!id){id=elem[_V_.expando]=_V_.guid++;_V_.cache[id]={}}return _V_.cache[id]},removeData:function(elem){var id=elem[_V_.expando];if(!id){return}delete _V_.cache[id];try{delete elem[_V_.expando]}catch(e){if(elem.removeAttribute){elem.removeAttribute(_V_.expando)}else{elem[_V_.expando]=null}}},proxy:function(context,fn){if(!fn.guid){fn.guid=_V_.guid++}var ret=function(){return fn.apply(context,arguments)};ret.guid=fn.guid;return ret},get:function(url,onSuccess,onError){var local=(url.indexOf("file:")==0||(window.location.href.indexOf("file:")==0&&url.indexOf("http:")==-1));if(typeof XMLHttpRequest=="undefined"){XMLHttpRequest=function(){try{return new ActiveXObject("Msxml2.XMLHTTP.6.0")}catch(e){}try{return new ActiveXObject("Msxml2.XMLHTTP.3.0")}catch(f){}try{return new ActiveXObject("Msxml2.XMLHTTP")}catch(g){}throw new Error("This browser does not support XMLHttpRequest.")}}var request=new XMLHttpRequest();try{request.open("GET",url)}catch(e){_V_.log("VideoJS XMLHttpRequest (open)",e);return false}request.onreadystatechange=_V_.proxy(this,function(){if(request.readyState==4){if(request.status==200||local&&request.status==0){onSuccess(request.responseText)}else{if(onError){onError()}}}});try{request.send()}catch(e){_V_.log("VideoJS XMLHttpRequest (send)",e);if(onError){onError(e)}}},setLocalStorage:function(key,value){var localStorage=localStorage||false;if(!localStorage){return}try{localStorage[key]=value}catch(e){if(e.code==22||e.code==1014){_V_.log("LocalStorage Full (VideoJS)",e)}else{_V_.log("LocalStorage Error (VideoJS)",e)}}}});_V_.log=function(){_V_.log.history=_V_.log.history||[];_V_.log.history.push(arguments);if(window.console){arguments.callee=arguments.callee.caller;var newarr=[].slice.call(arguments);(typeof console.log==="object"?_V_.log.apply.call(console.log,console,newarr):console.log.apply(console,newarr))}};(function(b){function c(){}for(var d="assert,count,debug,dir,dirxml,error,exception,group,groupCollapsed,groupEnd,info,log,timeStamp,profile,profileEnd,time,timeEnd,trace,warn".split(","),a;a=d.pop();){b[a]=b[a]||c}})((function(){try{console.log();return window.console}catch(err){return window.console={}}})());if("getBoundingClientRect" in document.documentElement){_V_.findPosX=function(el){var box;try{box=el.getBoundingClientRect()}catch(e){}if(!box){return 0}var docEl=document.documentElement,body=document.body,clientLeft=docEl.clientLeft||body.clientLeft||0,scrollLeft=window.pageXOffset||body.scrollLeft,left=box.left+scrollLeft-clientLeft;return left}}else{_V_.findPosX=function(el){var curleft=el.offsetLeft;while(el=obj.offsetParent){if(el.className.indexOf("video-js")==-1){}else{}curleft+=el.offsetLeft}return curleft}}(function(){var initializing=false,fnTest=/xyz/.test(function(){xyz})?/\b_super\b/:/.*/;_V_.Class=function(){};_V_.Class.extend=function(prop){var _super=this.prototype;initializing=true;var prototype=new this();initializing=false;for(var name in prop){prototype[name]=typeof prop[name]=="function"&&typeof _super[name]=="function"&&fnTest.test(prop[name])?(function(name,fn){return function(){var tmp=this._super;this._super=_super[name];var ret=fn.apply(this,arguments);this._super=tmp;return ret}})(name,prop[name]):prop[name]}function Class(){if(!initializing&&this.init){return this.init.apply(this,arguments)}else{if(!initializing){return arguments.callee.prototype.init()}}}Class.prototype=prototype;Class.constructor=Class;Class.extend=arguments.callee;return Class}})();_V_.Component=_V_.Class.extend({init:function(player,options){this.player=player;options=this.options=_V_.merge(this.options||{},options);if(options.el){this.el=options.el}else{this.el=this.createElement()}this.initComponents()},destroy:function(){},createElement:function(type,attrs){return _V_.createElement(type||"div",attrs)},buildCSSClass:function(){return""},initComponents:function(){var options=this.options;if(options&&options.components){this.eachProp(options.components,function(name,opts){var tempAdd=this.proxy(function(){this.addComponent(name,opts)});if(opts.loadEvent){this.one(opts.loadEvent,tempAdd)}else{tempAdd()}})}},addComponent:function(name,options){var componentClass,component;options=options||{};componentClass=options.componentClass||_V_.capitalize(name);component=new _V_[componentClass](this.player||this,options);this.el.appendChild(component.el);this[name]=component},show:function(){this.el.style.display="block"},hide:function(){this.el.style.display="none"},fadeIn:function(){this.removeClass("vjs-fade-out");this.addClass("vjs-fade-in")},fadeOut:function(){this.removeClass("vjs-fade-in");this.addClass("vjs-fade-out")},addClass:function(classToAdd){_V_.addClass(this.el,classToAdd)},removeClass:function(classToRemove){_V_.removeClass(this.el,classToRemove)},addEvent:function(type,fn){return _V_.addEvent(this.el,type,_V_.proxy(this,fn))},removeEvent:function(type,fn){return _V_.removeEvent(this.el,type,fn)},triggerEvent:function(type,e){return _V_.triggerEvent(this.el,type,e)},one:function(type,fn){_V_.one.call(this,this.el,type,fn)},ready:function(fn){if(!fn){return this}if(this.isReady){fn.call(this)}else{if(this.readyQueue===undefined){this.readyQueue=[]}this.readyQueue.push(fn)}return this},triggerReady:function(){this.isReady=true;if(this.readyQueue&&this.readyQueue.length>0){this.each(this.readyQueue,function(fn){fn.call(this)});this.readyQueue=[]}},each:function(arr,fn){_V_.each.call(this,arr,fn)},eachProp:function(obj,fn){_V_.eachProp.call(this,obj,fn)},extend:function(obj){_V_.merge(this,obj)},proxy:function(fn){return _V_.proxy(this,fn)}});_V_.Control=_V_.Component.extend({buildCSSClass:function(){return"vjs-control "+this._super()}});_V_.Button=_V_.Control.extend({init:function(player,options){this._super(player,options);this.addEvent("click",this.onClick);this.addEvent("focus",this.onFocus);this.addEvent("blur",this.onBlur)},createElement:function(type,attrs){attrs=_V_.merge({className:this.buildCSSClass(),innerHTML:'<div><span class="vjs-control-text">'+(this.buttonText||"Need Text")+"</span></div>",role:"button",tabIndex:0},attrs);return this._super(type,attrs)},onClick:function(){},onFocus:function(){_V_.addEvent(document,"keyup",_V_.proxy(this,this.onKeyPress))},onKeyPress:function(event){if(event.which==32||event.which==13){event.preventDefault();this.onClick()}},onBlur:function(){_V_.removeEvent(document,"keyup",_V_.proxy(this,this.onKeyPress))}});_V_.PlayButton=_V_.Button.extend({buttonText:"Play",buildCSSClass:function(){return"vjs-play-button "+this._super()},onClick:function(){this.player.play()}});_V_.PauseButton=_V_.Button.extend({buttonText:"Pause",buildCSSClass:function(){return"vjs-pause-button "+this._super()},onClick:function(){this.player.pause()}});_V_.PlayToggle=_V_.Button.extend({buttonText:"Play",init:function(player,options){this._super(player,options);player.addEvent("play",_V_.proxy(this,this.onPlay));player.addEvent("pause",_V_.proxy(this,this.onPause))},buildCSSClass:function(){return"vjs-play-control "+this._super()},onClick:function(){if(this.player.paused()){this.player.play()}else{this.player.pause()}},onPlay:function(){_V_.removeClass(this.el,"vjs-paused");_V_.addClass(this.el,"vjs-playing")},onPause:function(){_V_.removeClass(this.el,"vjs-playing");_V_.addClass(this.el,"vjs-paused")}});_V_.FullscreenToggle=_V_.Button.extend({buttonText:"Fullscreen",buildCSSClass:function(){return"vjs-fullscreen-control "+this._super()},onClick:function(){if(!this.player.isFullScreen){this.player.requestFullScreen()}else{this.player.cancelFullScreen()}}});_V_.BigPlayButton=_V_.Button.extend({init:function(player,options){this._super(player,options);player.addEvent("play",_V_.proxy(this,this.hide));player.addEvent("ended",_V_.proxy(this,this.show))},createElement:function(){return this._super("div",{className:"vjs-big-play-button",innerHTML:"<span></span>"})},onClick:function(){if(this.player.currentTime()){this.player.currentTime(0)}this.player.play()}});_V_.LoadingSpinner=_V_.Component.extend({init:function(player,options){this._super(player,options);player.addEvent("canplay",_V_.proxy(this,this.hide));player.addEvent("canplaythrough",_V_.proxy(this,this.hide));player.addEvent("playing",_V_.proxy(this,this.hide));player.addEvent("seeking",_V_.proxy(this,this.show));player.addEvent("error",_V_.proxy(this,this.show));player.addEvent("waiting",_V_.proxy(this,this.show))},createElement:function(){var classNameSpinner,innerHtmlSpinner;if(typeof this.player.el.style.WebkitBorderRadius=="string"||typeof this.player.el.style.MozBorderRadius=="string"||typeof this.player.el.style.KhtmlBorderRadius=="string"||typeof this.player.el.style.borderRadius=="string"){classNameSpinner="vjs-loading-spinner";innerHtmlSpinner="<div class='ball1'></div><div class='ball2'></div><div class='ball3'></div><div class='ball4'></div><div class='ball5'></div><div class='ball6'></div><div class='ball7'></div><div class='ball8'></div>"}else{classNameSpinner="vjs-loading-spinner-fallback";innerHtmlSpinner=""}return this._super("div",{className:classNameSpinner,innerHTML:innerHtmlSpinner})}});_V_.ControlBar=_V_.Component.extend({options:{loadEvent:"play",components:{playToggle:{},fullscreenToggle:{},currentTimeDisplay:{},timeDivider:{},durationDisplay:{},remainingTimeDisplay:{},progressControl:{},volumeControl:{},muteToggle:{}}},init:function(player,options){this._super(player,options);player.addEvent("play",this.proxy(function(){this.fadeIn();this.player.addEvent("mouseover",this.proxy(this.fadeIn));this.player.addEvent("mouseout",this.proxy(this.fadeOut))}))},createElement:function(){return _V_.createElement("div",{className:"vjs-controls"})},fadeIn:function(){this._super();this.player.triggerEvent("controlsvisible")},fadeOut:function(){this._super();this.player.triggerEvent("controlshidden")}});_V_.CurrentTimeDisplay=_V_.Component.extend({init:function(player,options){this._super(player,options);player.addEvent("timeupdate",_V_.proxy(this,this.updateContent))},createElement:function(){var el=this._super("div",{className:"vjs-current-time vjs-time-controls vjs-control"});this.content=_V_.createElement("div",{className:"vjs-current-time-display",innerHTML:"0:00"});el.appendChild(_V_.createElement("div").appendChild(this.content));return el},updateContent:function(){var time=(this.player.scrubbing)?this.player.values.currentTime:this.player.currentTime();this.content.innerHTML=_V_.formatTime(time,this.player.duration())}});_V_.DurationDisplay=_V_.Component.extend({init:function(player,options){this._super(player,options);player.addEvent("timeupdate",_V_.proxy(this,this.updateContent))},createElement:function(){var el=this._super("div",{className:"vjs-duration vjs-time-controls vjs-control"});this.content=_V_.createElement("div",{className:"vjs-duration-display",innerHTML:"0:00"});el.appendChild(_V_.createElement("div").appendChild(this.content));return el},updateContent:function(){if(this.player.duration()){this.content.innerHTML=_V_.formatTime(this.player.duration())}}});_V_.TimeDivider=_V_.Component.extend({createElement:function(){return this._super("div",{className:"vjs-time-divider",innerHTML:"<div><span>/</span></div>"})}});_V_.RemainingTimeDisplay=_V_.Component.extend({init:function(player,options){this._super(player,options);player.addEvent("timeupdate",_V_.proxy(this,this.updateContent))},createElement:function(){var el=this._super("div",{className:"vjs-remaining-time vjs-time-controls vjs-control"});this.content=_V_.createElement("div",{className:"vjs-remaining-time-display",innerHTML:"-0:00"});el.appendChild(_V_.createElement("div").appendChild(this.content));return el},updateContent:function(){if(this.player.duration()){this.content.innerHTML="-"+_V_.formatTime(this.player.remainingTime())}}});_V_.Slider=_V_.Component.extend({init:function(player,options){this._super(player,options);player.addEvent(this.playerEvent,_V_.proxy(this,this.update));this.addEvent("mousedown",this.onMouseDown);this.addEvent("focus",this.onFocus);this.addEvent("blur",this.onBlur);this.player.addEvent("controlsvisible",this.proxy(this.update));this.update()},createElement:function(type,attrs){attrs=_V_.merge({role:"slider","aria-valuenow":0,"aria-valuemin":0,"aria-valuemax":100,tabIndex:0},attrs);return this._super(type,attrs)},onMouseDown:function(event){event.preventDefault();_V_.blockTextSelection();_V_.addEvent(document,"mousemove",_V_.proxy(this,this.onMouseMove));_V_.addEvent(document,"mouseup",_V_.proxy(this,this.onMouseUp));this.onMouseMove(event)},onMouseUp:function(event){_V_.unblockTextSelection();_V_.removeEvent(document,"mousemove",this.onMouseMove,false);_V_.removeEvent(document,"mouseup",this.onMouseUp,false);this.update()},update:function(){var barProgress,progress=this.getPercent();handle=this.handle,bar=this.bar;if(isNaN(progress)){progress=0}barProgress=progress;if(handle){var box=this.el,boxWidth=box.offsetWidth,handleWidth=handle.el.offsetWidth,handlePercent=(handleWidth)?handleWidth/boxWidth:0,boxAdjustedPercent=1-handlePercent;adjustedProgress=progress*boxAdjustedPercent,barProgress=adjustedProgress+(handlePercent/2);handle.el.style.left=_V_.round(adjustedProgress*100,2)+"%"}bar.el.style.width=_V_.round(barProgress*100,2)+"%"},calculateDistance:function(event){var box=this.el,boxX=_V_.findPosX(box),boxW=box.offsetWidth,handle=this.handle;if(handle){var handleW=handle.el.offsetWidth;boxX=boxX+(handleW/2);boxW=boxW-handleW}return Math.max(0,Math.min(1,(event.pageX-boxX)/boxW))},onFocus:function(event){_V_.addEvent(document,"keyup",_V_.proxy(this,this.onKeyPress))},onKeyPress:function(event){if(event.which==37){event.preventDefault();this.stepBack()}else{if(event.which==39){event.preventDefault();this.stepForward()}}},onBlur:function(event){_V_.removeEvent(document,"keyup",_V_.proxy(this,this.onKeyPress))}});_V_.ProgressControl=_V_.Component.extend({options:{components:{seekBar:{}}},createElement:function(){return this._super("div",{className:"vjs-progress-control vjs-control"})}});_V_.SeekBar=_V_.Slider.extend({options:{components:{loadProgressBar:{},bar:{componentClass:"PlayProgressBar"},handle:{componentClass:"SeekHandle"}}},playerEvent:"timeupdate",init:function(player,options){this._super(player,options)},createElement:function(){return this._super("div",{className:"vjs-progress-holder"})},getPercent:function(){return this.player.currentTime()/this.player.duration()},onMouseDown:function(event){this._super(event);this.player.scrubbing=true;this.videoWasPlaying=!this.player.paused();this.player.pause()},onMouseMove:function(event){var newTime=this.calculateDistance(event)*this.player.duration();if(newTime==this.player.duration()){newTime=newTime-0.1}this.player.currentTime(newTime)},onMouseUp:function(event){this._super(event);this.player.scrubbing=false;if(this.videoWasPlaying){this.player.play()}},stepForward:function(){this.player.currentTime(this.player.currentTime()+1)},stepBack:function(){this.player.currentTime(this.player.currentTime()-1)}});_V_.LoadProgressBar=_V_.Component.extend({init:function(player,options){this._super(player,options);player.addEvent("progress",_V_.proxy(this,this.update))},createElement:function(){return this._super("div",{className:"vjs-load-progress",innerHTML:'<span class="vjs-control-text">Loaded: 0%</span>'})},update:function(){if(this.el.style){this.el.style.width=_V_.round(this.player.bufferedPercent()*100,2)+"%"}}});_V_.PlayProgressBar=_V_.Component.extend({createElement:function(){return this._super("div",{className:"vjs-play-progress",innerHTML:'<span class="vjs-control-text">Progress: 0%</span>'})}});_V_.SeekHandle=_V_.Component.extend({createElement:function(){return this._super("div",{className:"vjs-seek-handle",innerHTML:'<span class="vjs-control-text">00:00</span>'})}});_V_.VolumeControl=_V_.Component.extend({options:{components:{volumeBar:{}}},createElement:function(){return this._super("div",{className:"vjs-volume-control vjs-control"})}});_V_.VolumeBar=_V_.Slider.extend({options:{components:{bar:{componentClass:"VolumeLevel"},handle:{componentClass:"VolumeHandle"}}},playerEvent:"volumechange",createElement:function(){return this._super("div",{className:"vjs-volume-bar"})},onMouseMove:function(event){this.player.volume(this.calculateDistance(event))},getPercent:function(){return this.player.volume()},stepForward:function(){this.player.volume(this.player.volume()+0.1)},stepBack:function(){this.player.volume(this.player.volume()-0.1)}});_V_.VolumeLevel=_V_.Component.extend({createElement:function(){return this._super("div",{className:"vjs-volume-level",innerHTML:'<span class="vjs-control-text"></span>'})}});_V_.VolumeHandle=_V_.Component.extend({createElement:function(){return this._super("div",{className:"vjs-volume-handle",innerHTML:'<span class="vjs-control-text"></span>'})}});_V_.MuteToggle=_V_.Button.extend({init:function(player,options){this._super(player,options);player.addEvent("volumechange",_V_.proxy(this,this.update))},createElement:function(){return this._super("div",{className:"vjs-mute-control vjs-control",innerHTML:'<div><span class="vjs-control-text">Mute</span></div>'})},onClick:function(event){this.player.muted(this.player.muted()?false:true)},update:function(event){var vol=this.player.volume(),level=3;if(vol==0||this.player.muted()){level=0}else{if(vol<0.33){level=1}else{if(vol<0.67){level=2}}}_V_.each.call(this,[0,1,2,3],function(i){_V_.removeClass(this.el,"vjs-vol-"+i)});_V_.addClass(this.el,"vjs-vol-"+level)}});_V_.Poster=_V_.Button.extend({init:function(player,options){this._super(player,options);if(!this.player.options.poster){this.hide()}player.addEvent("play",_V_.proxy(this,this.hide))},createElement:function(){return _V_.createElement("img",{className:"vjs-poster",src:this.player.options.poster,tabIndex:-1})},onClick:function(){this.player.play()}});_V_.TextTrackDisplay=_V_.Component.extend({init:function(player,options){this._super(player,options);player.addEvent(this.trackType+"update",_V_.proxy(this,this.update))},createElement:function(){return this._super("div",{className:"vjs-"+this.trackType})},update:function(){this.el.innerHTML=this.player.textTrackValue(this.trackType)}});_V_.SubtitlesDisplay=_V_.TextTrackDisplay.extend({trackType:"subtitles"});_V_.CaptionsDisplay=_V_.TextTrackDisplay.extend({trackType:"captions"});_V_.ChaptersDisplay=_V_.TextTrackDisplay.extend({trackType:"chapters"});_V_.DescriptionsDisplay=_V_.TextTrackDisplay.extend({trackType:"descriptions"});if(!Array.prototype.indexOf){Array.prototype.indexOf=function(searchElement){if(this===void 0||this===null){throw new TypeError()}var t=Object(this);var len=t.length>>>0;if(len===0){return -1}var n=0;if(arguments.length>0){n=Number(arguments[1]);if(n!==n){n=0}else{if(n!==0&&n!==(1/0)&&n!==-(1/0)){n=(n>0||-1)*Math.floor(Math.abs(n))}}}if(n>=len){return -1}var k=n>=0?n:Math.max(len-Math.abs(n),0);for(;k<len;k++){if(k in t&&t[k]===searchElement){return k}}return -1}}_V_.extend({addEvent:function(elem,type,fn){var data=_V_.getData(elem),handlers;if(data&&!data.handler){data.handler=function(event){event=_V_.fixEvent(event);var handlers=_V_.getData(elem).events[event.type];if(handlers){var handlersCopy=[];_V_.each(handlers,function(handler,i){handlersCopy[i]=handler});for(var i=0,l=handlersCopy.length;i<l;i++){handlersCopy[i].call(elem,event)}}}}if(!data.events){data.events={}}handlers=data.events[type];if(!handlers){handlers=data.events[type]=[];if(document.addEventListener){elem.addEventListener(type,data.handler,false)}else{if(document.attachEvent){elem.attachEvent("on"+type,data.handler)}}}if(!fn.guid){fn.guid=_V_.guid++}handlers.push(fn)},removeEvent:function(elem,type,fn){var data=_V_.getData(elem),handlers;if(!data.events){return}if(!type){for(type in data.events){_V_.cleanUpEvents(elem,type)}return}handlers=data.events[type];if(!handlers){return}if(fn&&fn.guid){for(var i=0;i<handlers.length;i++){if(handlers[i].guid===fn.guid){handlers.splice(i--,1)}}}_V_.cleanUpEvents(elem,type)},cleanUpEvents:function(elem,type){var data=_V_.getData(elem);if(data.events[type].length===0){delete data.events[type];if(document.removeEventListener){elem.removeEventListener(type,data.handler,false)}else{if(document.detachEvent){elem.detachEvent("on"+type,data.handler)}}}if(_V_.isEmpty(data.events)){delete data.events;delete data.handler}if(_V_.isEmpty(data)){_V_.removeData(elem)}},fixEvent:function(event){if(event[_V_.expando]){return event}var originalEvent=event;event=new _V_.Event(originalEvent);for(var i=_V_.Event.props.length,prop;i;){prop=_V_.Event.props[--i];event[prop]=originalEvent[prop]}if(!event.target){event.target=event.srcElement||document}if(event.target.nodeType===3){event.target=event.target.parentNode}if(!event.relatedTarget&&event.fromElement){event.relatedTarget=event.fromElement===event.target?event.toElement:event.fromElement}if(event.pageX==null&&event.clientX!=null){var eventDocument=event.target.ownerDocument||document,doc=eventDocument.documentElement,body=eventDocument.body;event.pageX=event.clientX+(doc&&doc.scrollLeft||body&&body.scrollLeft||0)-(doc&&doc.clientLeft||body&&body.clientLeft||0);event.pageY=event.clientY+(doc&&doc.scrollTop||body&&body.scrollTop||0)-(doc&&doc.clientTop||body&&body.clientTop||0)}if(event.which==null&&(event.charCode!=null||event.keyCode!=null)){event.which=event.charCode!=null?event.charCode:event.keyCode}if(!event.metaKey&&event.ctrlKey){event.metaKey=event.ctrlKey}if(!event.which&&event.button!==undefined){event.which=(event.button&1?1:(event.button&2?3:(event.button&4?2:0)))}return event},triggerEvent:function(elem,event){var data=_V_.getData(elem),parent=elem.parentNode||elem.ownerDocument,type=event.type||event,handler;if(data){handler=data.handler}event=typeof event==="object"?event[_V_.expando]?event:new _V_.Event(type,event):new _V_.Event(type);event.type=type;if(handler){handler.call(elem,event)}event.result=undefined;event.target=elem},one:function(elem,type,fn){_V_.addEvent(elem,type,function(){_V_.removeEvent(elem,type,arguments.callee);fn.apply(this,arguments)})}});_V_.Event=function(src,props){if(src&&src.type){this.originalEvent=src;this.type=src.type;this.isDefaultPrevented=(src.defaultPrevented||src.returnValue===false||src.getPreventDefault&&src.getPreventDefault())?returnTrue:returnFalse}else{this.type=src}if(props){_V_.merge(this,props)}this.timeStamp=(new Date).getTime();this[_V_.expando]=true};_V_.Event.prototype={preventDefault:function(){this.isDefaultPrevented=returnTrue;var e=this.originalEvent;if(!e){return}if(e.preventDefault){e.preventDefault()}else{e.returnValue=false}},stopPropagation:function(){this.isPropagationStopped=returnTrue;var e=this.originalEvent;if(!e){return}if(e.stopPropagation){e.stopPropagation()}e.cancelBubble=true},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=returnTrue;this.stopPropagation()},isDefaultPrevented:returnFalse,isPropagationStopped:returnFalse,isImmediatePropagationStopped:returnFalse};_V_.Event.props="altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode metaKey newValue offsetX offsetY pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" ");function returnTrue(){return true}function returnFalse(){return false}var JSON;if(!JSON){JSON={}}(function(){var cx=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;if(typeof JSON.parse!=="function"){JSON.parse=function(text,reviver){var j;function walk(holder,key){var k,v,value=holder[key];if(value&&typeof value==="object"){for(k in value){if(Object.prototype.hasOwnProperty.call(value,k)){v=walk(value,k);if(v!==undefined){value[k]=v}else{delete value[k]}}}}return reviver.call(holder,key,value)}text=String(text);cx.lastIndex=0;if(cx.test(text)){text=text.replace(cx,function(a){return"\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4)})}if(/^[\],:{}\s]*$/.test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,"@").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,"]").replace(/(?:^|:|,)(?:\s*\[)+/g,""))){j=eval("("+text+")");return typeof reviver==="function"?walk({"":j},""):j}throw new SyntaxError("JSON.parse")}}}());_V_.Player=_V_.Component.extend({init:function(tag,addOptions,ready){this.tag=tag;var el=this.el=_V_.createElement("div"),options=this.options={},width=options.width=tag.getAttribute("width"),height=options.height=tag.getAttribute("height"),initWidth=width||300,initHeight=height||150;tag.player=el.player=this;this.ready(ready);tag.parentNode.insertBefore(el,tag);el.appendChild(tag);el.id=this.id=tag.id;el.className=tag.className;tag.id+="_html5_api";tag.className="vjs-tech";_V_.players[el.id]=this;el.setAttribute("width",initWidth);el.setAttribute("height",initHeight);el.style.width=initWidth+"px";el.style.height=initHeight+"px";tag.removeAttribute("width");tag.removeAttribute("height");_V_.merge(options,_V_.options);_V_.merge(options,this.getVideoTagSettings());_V_.merge(options,addOptions);tag.removeAttribute("controls");tag.removeAttribute("poster");if(tag.hasChildNodes()){for(var i=0,j=tag.childNodes;i<j.length;i++){if(j[i].nodeName=="SOURCE"||j[i].nodeName=="TRACK"){tag.removeChild(j[i])}}}this.techs={};this.values={};this.addClass("vjs-paused");this.addEvent("ended",this.onEnded);this.addEvent("play",this.onPlay);this.addEvent("pause",this.onPause);this.addEvent("error",this.onError);if(options.controls){this.ready(function(){this.initComponents()})}if(!options.sources||options.sources.length==0){for(var i=0,j=options.techOrder;i<j.length;i++){var techName=j[i],tech=_V_[techName];if(tech.isSupported()){this.loadTech(techName);break}}}else{this.src(options.sources)}},values:{},destroy:function(){this.stopTrackingProgress();this.stopTrackingCurrentTime();delete _V_.players[this.id]},createElement:function(type,options){},getVideoTagSettings:function(){var options={sources:[],tracks:[]};options.src=this.tag.getAttribute("src");options.controls=this.tag.getAttribute("controls")!==null;options.poster=this.tag.getAttribute("poster");options.preload=this.tag.getAttribute("preload");options.autoplay=this.tag.getAttribute("autoplay")!==null;options.loop=this.tag.getAttribute("loop")!==null;options.muted=this.tag.getAttribute("muted")!==null;if(this.tag.hasChildNodes()){for(var c,i=0,j=this.tag.childNodes;i<j.length;i++){c=j[i];if(c.nodeName=="SOURCE"){options.sources.push({src:c.getAttribute("src"),type:c.getAttribute("type"),media:c.getAttribute("media"),title:c.getAttribute("title")})}if(c.nodeName=="TRACK"){options.tracks.push(new _V_.Track({src:c.getAttribute("src"),kind:c.getAttribute("kind"),srclang:c.getAttribute("srclang"),label:c.getAttribute("label"),"default":c.getAttribute("default")!==null,title:c.getAttribute("title")},this))}}}return options},loadTech:function(techName,source){if(this.tech){this.unloadTech()}else{if(techName!="html5"&&this.tag){this.el.removeChild(this.tag);this.tag=false}}this.techName=techName;this.isReady=false;var techReady=function(){this.player.triggerReady();if(!this.support.progressEvent){this.player.manualProgressOn()}if(!this.support.timeupdateEvent){this.player.manualTimeUpdatesOn()}};var techOptions=_V_.merge({source:source,parentEl:this.el},this.options[techName]);if(source){if(source.src==this.values.src&&this.values.currentTime>0){techOptions.startTime=this.values.currentTime}this.values.src=source.src}this.tech=new _V_[techName](this,techOptions);this.tech.ready(techReady)},unloadTech:function(){this.tech.destroy();if(this.manualProgress){this.manualProgressOff()}if(this.manualTimeUpdates){this.manualTimeUpdatesOff()}this.tech=false},manualProgressOn:function(){this.manualProgress=true;this.trackProgress();this.tech.addEvent("progress",function(){this.removeEvent("progress",arguments.callee);this.support.progressEvent=true;this.player.manualProgressOff()})},manualProgressOff:function(){this.manualProgress=false;this.stopTrackingProgress()},trackProgress:function(){this.progressInterval=setInterval(_V_.proxy(this,function(){if(this.values.bufferEnd<this.buffered().end(0)){this.triggerEvent("progress")}else{if(this.bufferedPercent()==1){this.stopTrackingProgress();this.triggerEvent("progress")}}}),500)},stopTrackingProgress:function(){clearInterval(this.progressInterval)},manualTimeUpdatesOn:function(){this.manualTimeUpdates=true;this.addEvent("play",this.trackCurrentTime);this.addEvent("pause",this.stopTrackingCurrentTime);this.tech.addEvent("timeupdate",function(){this.removeEvent("timeupdate",arguments.callee);this.support.timeupdateEvent=true;this.player.manualTimeUpdatesOff()})},manualTimeUpdatesOff:function(){this.manualTimeUpdates=false;this.stopTrackingCurrentTime();this.removeEvent("play",this.trackCurrentTime);this.removeEvent("pause",this.stopTrackingCurrentTime)},trackCurrentTime:function(){if(this.currentTimeInterval){this.stopTrackingCurrentTime()}this.currentTimeInterval=setInterval(_V_.proxy(this,function(){this.triggerEvent("timeupdate")}),250)},stopTrackingCurrentTime:function(){clearInterval(this.currentTimeInterval)},onEnded:function(){if(this.options.loop){this.currentTime(0);this.play()}else{this.pause();this.currentTime(0);this.pause()}},onPlay:function(){_V_.removeClass(this.el,"vjs-paused");_V_.addClass(this.el,"vjs-playing")},onPause:function(){_V_.removeClass(this.el,"vjs-playing");_V_.addClass(this.el,"vjs-paused")},onError:function(e){_V_.log("Video Error",e)},apiCall:function(method,arg){if(this.isReady){return this.tech[method](arg)}else{_V_.log("The playback technology API is not ready yet. Use player.ready(myFunction). ["+method+"]",arguments.callee.caller.arguments.callee.caller.arguments.callee.caller);return false}},play:function(){this.apiCall("play");return this},pause:function(){this.apiCall("pause");return this},paused:function(){return this.apiCall("paused")},currentTime:function(seconds){if(seconds!==undefined){this.values.lastSetCurrentTime=seconds;this.apiCall("setCurrentTime",seconds);if(this.manualTimeUpdates){this.triggerEvent("timeupdate")}return this}return this.values.currentTime=this.apiCall("currentTime")},duration:function(){return this.apiCall("duration")},remainingTime:function(){return this.duration()-this.currentTime()},buffered:function(){var buffered=this.apiCall("buffered"),start=0,end=this.values.bufferEnd=this.values.bufferEnd||0,timeRange;if(buffered&&buffered.length>0&&buffered.end(0)!==end){end=buffered.end(0);this.values.bufferEnd=end}return _V_.createTimeRange(start,end)},bufferedPercent:function(){return(this.duration())?this.buffered().end(0)/this.duration():0},volume:function(percentAsDecimal){if(percentAsDecimal!==undefined){var vol=Math.max(0,Math.min(1,parseFloat(percentAsDecimal)));this.values.volume=vol;this.apiCall("setVolume",vol);_V_.setLocalStorage("volume",vol);return this}return this.apiCall("volume")},muted:function(muted){if(muted!==undefined){this.apiCall("setMuted",muted);return this}return this.apiCall("muted")},width:function(width,skipListeners){if(width!==undefined){this.el.width=width;this.el.style.width=width+"px";if(!skipListeners){this.triggerEvent("resize")}return this}return parseInt(this.el.getAttribute("width"))},height:function(height){if(height!==undefined){this.el.height=height;this.el.style.height=height+"px";this.triggerEvent("resize");return this}return parseInt(this.el.getAttribute("height"))},size:function(width,height){return this.width(width,true).height(height)},supportsFullScreen:function(){return this.apiCall("supportsFullScreen")},requestFullScreen:function(){var requestFullScreen=_V_.support.requestFullScreen;this.isFullScreen=true;if(requestFullScreen){if(this.tech.support.fullscreenResize===false&&this.options.flash.iFrameMode!=true){this.pause();this.unloadTech();_V_.addEvent(document,requestFullScreen.eventName,this.proxy(function(){_V_.removeEvent(document,requestFullScreen.eventName,arguments.callee);this.loadTech(this.techName,{src:this.values.src})}));this.el[requestFullScreen.requestFn]()}else{this.el[requestFullScreen.requestFn]()}_V_.addEvent(document,requestFullScreen.eventName,this.proxy(function(){this.isFullScreen=document[requestFullScreen.isFullScreen]}))}else{if(this.tech.supportsFullScreen()){this.apiCall("enterFullScreen")}else{this.enterFullWindow()}}this.triggerEvent("fullscreenchange");return this},cancelFullScreen:function(){var requestFullScreen=_V_.support.requestFullScreen;if(requestFullScreen){if(this.tech.support.fullscreenResize===false&&this.options.flash.iFrameMode!=true){this.pause();this.unloadTech();_V_.addEvent(document,requestFullScreen.eventName,this.proxy(function(){_V_.removeEvent(document,requestFullScreen.eventName,arguments.callee);this.loadTech(this.techName,{src:this.values.src})}));document[requestFullScreen.cancelFn]()}else{document[requestFullScreen.cancelFn]()}}else{if(this.tech.supportsFullScreen()){this.apiCall("exitFullScreen")}else{this.exitFullWindow()}}this.isFullScreen=false;this.triggerEvent("fullscreenchange");return this},enterFullWindow:function(){this.isFullWindow=true;this.docOrigOverflow=document.documentElement.style.overflow;_V_.addEvent(document,"keydown",_V_.proxy(this,this.fullWindowOnEscKey));document.documentElement.style.overflow="hidden";_V_.addClass(document.body,"vjs-full-window");_V_.addClass(this.el,"vjs-fullscreen");this.triggerEvent("enterFullWindow")},fullWindowOnEscKey:function(event){if(event.keyCode==27){if(this.isFullScreen==true){this.cancelFullScreen()}else{this.exitFullWindow()}}},exitFullWindow:function(){this.isFullWindow=false;_V_.removeEvent(document,"keydown",this.fullWindowOnEscKey);document.documentElement.style.overflow=this.docOrigOverflow;_V_.removeClass(document.body,"vjs-full-window");_V_.removeClass(this.el,"vjs-fullscreen");this.triggerEvent("exitFullWindow")},src:function(source){if(source instanceof Array){var sources=source;techLoop:for(var i=0,j=this.options.techOrder;i<j.length;i++){var techName=j[i],tech=_V_[techName];if(tech.isSupported()){for(var a=0,b=sources;a<b.length;a++){var source=b[a];if(tech.canPlaySource.call(this,source)){if(techName==this.techName){this.src(source)}else{this.loadTech(techName,source)}break techLoop}}}}}else{if(source instanceof Object){if(_V_[this.techName].canPlaySource(source)){this.src(source.src)}else{this.src([source])}}else{this.values.src=source;if(!this.isReady){this.ready(function(){this.src(source)})}else{this.apiCall("src",source);if(this.options.preload=="auto"){this.load()}if(this.options.autoplay){this.play()}}}}return this},load:function(){this.apiCall("load");return this},currentSrc:function(){return this.apiCall("currentSrc")},textTrackValue:function(kind,value){if(value!==undefined){this.values[kind]=value;this.triggerEvent(kind+"update");return this}return this.values[kind]},preload:function(value){if(value!==undefined){this.apiCall("setPreload",value);this.options.preload=value;return this}return this.apiCall("preload",value)},autoplay:function(value){if(value!==undefined){this.apiCall("setAutoplay",value);this.options.autoplay=value;return this}return this.apiCall("autoplay",value)},loop:function(value){if(value!==undefined){this.apiCall("setLoop",value);this.options.loop=value;return this}return this.apiCall("loop",value)},controls:function(){return this.options.controls},textTracks:function(){return this.options.tracks},poster:function(){return this.apiCall("poster")},error:function(){return this.apiCall("error")},networkState:function(){return this.apiCall("networkState")},readyState:function(){return this.apiCall("readyState")},seeking:function(){return this.apiCall("seeking")},initialTime:function(){return this.apiCall("initialTime")},startOffsetTime:function(){return this.apiCall("startOffsetTime")},played:function(){return this.apiCall("played")},seekable:function(){return this.apiCall("seekable")},ended:function(){return this.apiCall("ended")},videoTracks:function(){return this.apiCall("videoTracks")},audioTracks:function(){return this.apiCall("audioTracks")},videoWidth:function(){return this.apiCall("videoWidth")},videoHeight:function(){return this.apiCall("videoHeight")},defaultPlaybackRate:function(){return this.apiCall("defaultPlaybackRate")},playbackRate:function(){return this.apiCall("playbackRate")},controls:function(){return this.apiCall("controls")},defaultMuted:function(){return this.apiCall("defaultMuted")}});(function(){var requestFn,cancelFn,eventName,isFullScreen,playerProto=_V_.Player.prototype;if(document.cancelFullscreen!==undefined){requestFn="requestFullscreen";cancelFn="exitFullscreen";eventName="fullscreenchange";isFullScreen="fullScreen"}else{_V_.each(["moz","webkit"],function(prefix){if((prefix!="moz"||document.mozFullScreenEnabled)&&document[prefix+"CancelFullScreen"]!==undefined){requestFn=prefix+"RequestFullScreen";cancelFn=prefix+"CancelFullScreen";eventName=prefix+"fullscreenchange";if(prefix=="webkit"){isFullScreen=prefix+"IsFullScreen"}else{_V_.log("moz here");isFullScreen=prefix+"FullScreen"}}})}if(requestFn){_V_.support.requestFullScreen={requestFn:requestFn,cancelFn:cancelFn,eventName:eventName,isFullScreen:isFullScreen}}})();_V_.PlaybackTech=_V_.Component.extend({init:function(player,options){},onClick:function(){if(this.player.options.controls){_V_.PlayToggle.prototype.onClick.call(this)}}});_V_.apiMethods="play,pause,paused,currentTime,setCurrentTime,duration,buffered,volume,setVolume,muted,setMuted,width,height,supportsFullScreen,enterFullScreen,src,load,currentSrc,preload,setPreload,autoplay,setAutoplay,loop,setLoop,error,networkState,readyState,seeking,initialTime,startOffsetTime,played,seekable,ended,videoTracks,audioTracks,videoWidth,videoHeight,textTracks,defaultPlaybackRate,playbackRate,mediaGroup,controller,controls,defaultMuted".split(",");_V_.each(_V_.apiMethods,function(methodName){_V_.PlaybackTech.prototype[methodName]=function(){throw new Error("The '"+method+"' method is not available on the playback technology's API")}});_V_.html5=_V_.PlaybackTech.extend({init:function(player,options,ready){this.player=player;this.el=this.createElement();this.ready(ready);this.addEvent("click",this.proxy(this.onClick));var source=options.source;if(source&&this.el.currentSrc==source.src){player.triggerEvent("loadstart")}else{if(source){this.el.src=source.src}}player.ready(function(){if(this.options.autoplay&&this.paused()){this.tag.poster=null;this.play()}});this.setupTriggers();this.triggerReady()},destroy:function(){this.player.tag=false;this.removeTriggers();this.el.parentNode.removeChild(this.el)},createElement:function(){var html5=_V_.html5,player=this.player,el=player.tag,newEl;if(!el||this.support.movingElementInDOM===false){if(el){player.el.removeChild(el)}newEl=_V_.createElement("video",{id:el.id||player.el.id+"_html5_api",className:el.className||"vjs-tech"});el=newEl;_V_.insertFirst(el,player.el)}_V_.each(["autoplay","preload","loop","muted"],function(attr){el[attr]=player.options[attr]},this);return el},setupTriggers:function(){_V_.each.call(this,_V_.html5.events,function(type){_V_.addEvent(this.el,type,_V_.proxy(this.player,this.eventHandler))})},removeTriggers:function(){_V_.each.call(this,_V_.html5.events,function(type){_V_.removeEvent(this.el,type,_V_.proxy(this.player,this.eventHandler))})},eventHandler:function(e){e.stopPropagation();this.triggerEvent(e)},play:function(){this.el.play()},pause:function(){this.el.pause()},paused:function(){return this.el.paused},currentTime:function(){return this.el.currentTime},setCurrentTime:function(seconds){try{this.el.currentTime=seconds}catch(e){_V_.log(e,"Video isn't ready. (VideoJS)")}},duration:function(){return this.el.duration||0},buffered:function(){return this.el.buffered},volume:function(){return this.el.volume},setVolume:function(percentAsDecimal){this.el.volume=percentAsDecimal},muted:function(){return this.el.muted},setMuted:function(muted){this.el.muted=muted},width:function(){return this.el.offsetWidth},height:function(){return this.el.offsetHeight},supportsFullScreen:function(){if(typeof this.el.webkitEnterFullScreen=="function"){if(!navigator.userAgent.match("Chrome")&&!navigator.userAgent.match("Mac OS X 10.5")){return true}}return false},enterFullScreen:function(){try{this.el.webkitEnterFullScreen()}catch(e){if(e.code==11){_V_.log("VideoJS: Video not ready.")}}},src:function(src){this.el.src=src},load:function(){this.el.load()},currentSrc:function(){return this.el.currentSrc},preload:function(){return this.el.preload},setPreload:function(val){this.el.preload=val},autoplay:function(){return this.el.autoplay},setAutoplay:function(val){this.el.autoplay=val},loop:function(){return this.el.loop},setLoop:function(val){this.el.loop=val},error:function(){return this.el.error},seeking:function(){return this.el.seeking},ended:function(){return this.el.ended},controls:function(){return this.player.options.controls},defaultMuted:function(){return this.el.defaultMuted}});_V_.html5.isSupported=function(){return !!document.createElement("video").canPlayType};_V_.html5.canPlaySource=function(srcObj){return !!document.createElement("video").canPlayType(srcObj.type)};_V_.html5.events="loadstart,suspend,abort,error,emptied,stalled,loadedmetadata,loadeddata,canplay,canplaythrough,playing,waiting,seeking,seeked,ended,durationchange,timeupdate,progress,play,pause,ratechange,volumechange".split(",");_V_.html5.prototype.support={fullscreen:(typeof _V_.testVid.webkitEnterFullScreen!==undefined)?(!_V_.ua.match("Chrome")&&!_V_.ua.match("Mac OS X 10.5")?true:false):false,movingElementInDOM:!_V_.isIOS()};if(_V_.isAndroid()){if(_V_.androidVersion()<3){document.createElement("video").constructor.prototype.canPlayType=function(type){return(type&&type.toLowerCase().indexOf("video/mp4")!=-1)?"maybe":""}}}_V_.flash=_V_.PlaybackTech.extend({init:function(player,options){this.player=player;var source=options.source,parentEl=options.parentEl,placeHolder=this.el=_V_.createElement("div",{id:parentEl.id+"_temp_flash"}),objId=player.el.id+"_flash_api",playerOptions=player.options,flashVars=_V_.merge({readyFunction:"_V_.flash.onReady",eventProxyFunction:"_V_.flash.onEvent",errorEventProxyFunction:"_V_.flash.onError",autoplay:playerOptions.autoplay,preload:playerOptions.preload,loop:playerOptions.loop,muted:playerOptions.muted},options.flashVars),params=_V_.merge({wmode:"opaque",bgcolor:"#000000"},options.params),attributes=_V_.merge({id:objId,name:objId,"class":"vjs-tech"},options.attributes);if(source){flashVars.src=encodeURIComponent(source.src)}_V_.insertFirst(placeHolder,parentEl);if(options.startTime){this.ready(function(){this.load();this.play();this.currentTime(options.startTime)})}if(options.iFrameMode==true&&!_V_.isFF){var iFrm=_V_.createElement("iframe",{id:objId+"_iframe",name:objId+"_iframe",className:"vjs-tech",scrolling:"no",marginWidth:0,marginHeight:0,frameBorder:0});flashVars.readyFunction="ready";flashVars.eventProxyFunction="events";flashVars.errorEventProxyFunction="errors";_V_.addEvent(iFrm,"load",_V_.proxy(this,function(){var iDoc,objTag,swfLoc,iWin=iFrm.contentWindow,varString="";iDoc=iFrm.contentDocument?iFrm.contentDocument:iFrm.contentWindow.document;iDoc.write(_V_.flash.getEmbedCode(options.swf,flashVars,params,attributes));iWin.player=this.player;iWin.ready=_V_.proxy(this.player,function(currSwf){var el=iDoc.getElementById(currSwf),player=this,tech=player.tech;tech.el=el;_V_.addEvent(el,"click",tech.proxy(tech.onClick));_V_.flash.checkReady(tech)});iWin.events=_V_.proxy(this.player,function(swfID,eventName,other){var player=this;if(player&&player.techName=="flash"){player.triggerEvent(eventName)}});iWin.errors=_V_.proxy(this.player,function(swfID,eventName){_V_.log("Flash Error",eventName)})}));placeHolder.parentNode.replaceChild(iFrm,placeHolder)}else{_V_.flash.embed(options.swf,placeHolder,flashVars,params,attributes)}},destroy:function(){this.el.parentNode.removeChild(this.el)},play:function(){this.el.vjs_play()},pause:function(){this.el.vjs_pause()},src:function(src){this.el.vjs_src(src);if(this.player.autoplay){var tech=this;setTimeout(function(){tech.play()},0)}},load:function(){this.el.vjs_load()},poster:function(){this.el.vjs_getProperty("poster")},buffered:function(){return _V_.createTimeRange(0,this.el.vjs_getProperty("buffered"))},supportsFullScreen:function(){return false},enterFullScreen:function(){return false}});(function(){var api=_V_.flash.prototype,readWrite="preload,currentTime,defaultPlaybackRate,playbackRate,autoplay,loop,mediaGroup,controller,controls,volume,muted,defaultMuted".split(","),readOnly="error,currentSrc,networkState,readyState,seeking,initialTime,duration,startOffsetTime,paused,played,seekable,ended,videoTracks,audioTracks,videoWidth,videoHeight,textTracks".split(","),callOnly="load,play,pause".split(",");createSetter=function(attr){var attrUpper=attr.charAt(0).toUpperCase()+attr.slice(1);api["set"+attrUpper]=function(val){return this.el.vjs_setProperty(attr,val)}},createGetter=function(attr){api[attr]=function(){return this.el.vjs_getProperty(attr)}};_V_.each(readWrite,function(attr){createGetter(attr);createSetter(attr)});_V_.each(readOnly,function(attr){createGetter(attr)})})();_V_.flash.isSupported=function(){return _V_.flash.version()[0]>=10};_V_.flash.canPlaySource=function(srcObj){if(srcObj.type in _V_.flash.prototype.support.formats){return"maybe"}};_V_.flash.prototype.support={formats:{"video/flv":"FLV","video/x-flv":"FLV","video/mp4":"MP4","video/m4v":"MP4"},progressEvent:false,timeupdateEvent:false,fullscreenResize:false,parentResize:!(_V_.ua.match("Firefox"))};_V_.flash.onReady=function(currSwf){var el=_V_.el(currSwf);var player=el.player||el.parentNode.player,tech=player.tech;el.player=player;tech.el=el;tech.addEvent("click",tech.onClick);_V_.flash.checkReady(tech)};_V_.flash.checkReady=function(tech){if(tech.el.vjs_getProperty){tech.triggerReady()}else{setTimeout(function(){_V_.flash.checkReady(tech)},50)}};_V_.flash.onEvent=function(swfID,eventName){var player=_V_.el(swfID).player;player.triggerEvent(eventName)};_V_.flash.onError=function(swfID,err){_V_.log("Flash Error",err,swfID)};_V_.flash.version=function(){var version="0,0,0";try{version=new ActiveXObject("ShockwaveFlash.ShockwaveFlash").GetVariable("$version").replace(/\D+/g,",").match(/^,?(.+),?$/)[1]}catch(e){try{if(navigator.mimeTypes["application/x-shockwave-flash"].enabledPlugin){version=(navigator.plugins["Shockwave Flash 2.0"]||navigator.plugins["Shockwave Flash"]).description.replace(/\D+/g,",").match(/^,?(.+),?$/)[1]}}catch(e){}}return version.split(",")};_V_.flash.embed=function(swf,placeHolder,flashVars,params,attributes){var code=_V_.flash.getEmbedCode(swf,flashVars,params,attributes),obj=_V_.createElement("div",{innerHTML:code}).childNodes[0],par=placeHolder.parentNode;placeHolder.parentNode.replaceChild(obj,placeHolder);if(_V_.isIE()){var newObj=par.childNodes[0];setTimeout(function(){newObj.style.display="block"},1000)}return obj};_V_.flash.getEmbedCode=function(swf,flashVars,params,attributes){var objTag='<object type="application/x-shockwave-flash"',flashVarsString="",paramsString="";attrsString="";if(flashVars){_V_.eachProp(flashVars,function(key,val){flashVarsString+=(key+"="+val+"&")})}params=_V_.merge({movie:swf,flashvars:flashVarsString,allowScriptAccess:"always",allowNetworking:"all"},params);_V_.eachProp(params,function(key,val){paramsString+='<param name="'+key+'" value="'+val+'" />'});attributes=_V_.merge({data:swf,width:"100%",height:"100%"},attributes);_V_.eachProp(attributes,function(key,val){attrsString+=(key+'="'+val+'" ')});return objTag+attrsString+">"+paramsString+"</object>"};_V_.Track=function(attributes,player){this.player=player;this.src=attributes.src;this.kind=attributes.kind;this.srclang=attributes.srclang;this.label=attributes.label;this["default"]=attributes["default"];this.title=attributes.title;this.cues=[];this.currentCue=false;this.lastCueIndex=0;player.addEvent("timeupdate",_V_.proxy(this,this.update));player.addEvent("ended",_V_.proxy(this,function(){this.lastCueIndex=0}));_V_.get(attributes.src,_V_.proxy(this,this.parseCues))};_V_.Track.prototype={parseCues:function(srcContent){var cue,time,text,lines=srcContent.split("\n"),line="";for(var i=0;i<lines.length;i++){line=_V_.trim(lines[i]);if(line){cue={id:line,index:this.cues.length};line=_V_.trim(lines[++i]);time=line.split(" --> ");cue.startTime=this.parseCueTime(time[0]);cue.endTime=this.parseCueTime(time[1]);text=[];for(var j=i;j<lines.length;j++){line=_V_.trim(lines[++i]);if(!line){break}text.push(line)}cue.text=text.join("<br/>");this.cues.push(cue)}}},parseCueTime:function(timeText){var parts=timeText.split(":"),time=0;time+=parseFloat(parts[0])*60*60;time+=parseFloat(parts[1])*60;var seconds=parts[2].split(/\.|,/);time+=parseFloat(seconds[0]);ms=parseFloat(seconds[1]);if(ms){time+=ms/1000}return time},update:function(){if(this.cues&&this.cues.length>0){var time=this.player.currentTime();if(!this.currentCue||this.currentCue.startTime>=time||this.currentCue.endTime<time){var newSubIndex=false,reverse=(this.cues[this.lastCueIndex].startTime>time),i=this.lastCueIndex-(reverse?1:0);while(true){if(reverse){if(i<0||this.cues[i].endTime<time){break}if(this.cues[i].startTime<time){newSubIndex=i;break}i--}else{if(i>=this.cues.length||this.cues[i].startTime>time){break}if(this.cues[i].endTime>time){newSubIndex=i;break}i++}}if(newSubIndex!==false){this.currentCue=this.cues[newSubIndex];this.lastCueIndex=newSubIndex;this.updatePlayer(this.currentCue.text)}else{if(this.currentCue){this.currentCue=false;this.updatePlayer("")}}}}},updatePlayer:function(text){this.player.textTrackValue(this.kind,text)}};_V_.addEvent(window,"load",function(){_V_.windowLoaded=true});_V_.autoSetup();window.VideoJS=window._V_=VideoJS})(window); diff --git a/lazycelery.sh b/lazycelery.sh new file mode 120000 index 00000000..4ff15b1d --- /dev/null +++ b/lazycelery.sh @@ -0,0 +1 @@ +lazystarter.sh
\ No newline at end of file diff --git a/lazyserver.sh b/lazyserver.sh index 843993e6..4ff15b1d 100755..120000 --- a/lazyserver.sh +++ b/lazyserver.sh @@ -1,64 +1 @@ -#!/bin/sh - -# GNU MediaGoblin -- federated, autonomous media hosting -# Copyright (C) 2011 Free Software Foundation, Inc -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see <http://www.gnu.org/licenses/>. - -# -# This runs Mediagoblin using Paste with Celery set to always eager mode. -# - -if [ "$1" = "-h" ] -then - echo "$0 [-h] [-c paste.ini] [ARGS_to_paster ...]" - echo "" - echo " For example:" - echo " $0 -c fcgi.ini port_number=23371" - echo " or: $0 --server-name=fcgi --log-file=paste.log" - echo "" - echo " The configfile defaults to paste_local.ini," - echo " if that is readable, otherwise paste.ini." - exit 1 -fi - -PASTE_INI=paste.ini - -if [ -r paste_local.ini ] -then - PASTE_INI=paste_local.ini -fi - -if [ "$1" = "-c" ] -then - PASTE_INI="$2" - shift - shift -fi - -echo "Using paste config: $PASTE_INI" - -if [ -f ./bin/paster ]; then - echo "Using ./bin/paster"; - export PASTER="./bin/paster"; -elif which paster > /dev/null; then - echo "Using paster from \$PATH"; - export PASTER="paster"; -else - echo "No paster found, exiting! X_X"; - exit 1 -fi - -set -x -CELERY_ALWAYS_EAGER=true $PASTER serve $PASTE_INI "$@" --reload +lazystarter.sh
\ No newline at end of file diff --git a/lazystarter.sh b/lazystarter.sh new file mode 100755 index 00000000..d3770194 --- /dev/null +++ b/lazystarter.sh @@ -0,0 +1,82 @@ +#!/bin/sh + +# GNU MediaGoblin -- federated, autonomous media hosting +# Copyright (C) 2011 Free Software Foundation, Inc +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +selfname=$(basename "$0") +local_bin="./bin" +case "$selfname" in + lazyserver.sh) + starter_cmd=paster + ini_prefix=paste + ;; + lazycelery.sh) + starter_cmd=celeryd + ini_prefix=mediagoblin + ;; + *) + echo "Start this script with the name lazyserver.sh or lazycelery.sh">&2 + exit 1 + ;; +esac + +if [ "$1" = "-h" ]; then + echo "$0 [-h] [-c filename.ini] [ARGS_to_${starter_cmd} ...]" + echo "" + echo " For example:" + echo " $0 -c fcgi.ini port_number=23371" + echo " or: $0 --server-name=fcgi --log-file=paste.log" + echo "" + echo " The configfile defaults to ${ini_prefix}_local.ini," + echo " if that is readable, otherwise ${ini_prefix}.ini." + exit 1 +fi + +if [ "$1" = "-c" ]; then + ini_file=$2 + shift; shift +elif [ -r "${ini_prefix}_local.ini" ]; then + ini_file="${ini_prefix}_local.ini" +else + ini_file="${ini_prefix}.ini" +fi + +echo "Using ${starter_cmd} config: ${ini_file}" + +if [ -f "${local_bin}/${starter_cmd}" ]; then + echo "Using ${local_bin}/${starter_cmd}" + starter="${local_bin}/${starter_cmd}" +elif which "${starter_cmd}" > /dev/null; then + echo "Using ${starter_cmd} from \$PATH" + starter=$starter_cmd +else + echo "No ${starter_cmd} found, exiting! X_X" + exit 1 +fi + +set -x +export CELERY_ALWAYS_EAGER=true +case "$selfname" in + lazyserver.sh) + $starter serve "$ini_file" "$@" --reload + ;; + lazycelery.sh) + MEDIAGOBLIN_CONFIG="${ini_file}" \ + CELERY_CONFIG_MODULE=mediagoblin.init.celery.from_celery \ + $starter "$@" + ;; + *) exit 1 ;; +esac diff --git a/mediagoblin.ini b/mediagoblin.ini index dbde6e51..223f0f4a 100644 --- a/mediagoblin.ini +++ b/mediagoblin.ini @@ -5,6 +5,10 @@ direct_remote_path = /mgoblin_static/ email_sender_address = "notice@mediagoblin.example.org" +## Uncomment and change to your DB's appropiate setting. +## Default is a local sqlite db "mediagoblin.db". +# sql_engine = postgresql:///gmg + # set to false to enable sending notices email_debug_mode = true diff --git a/mediagoblin/_version.py b/mediagoblin/_version.py index 381d1270..fc1a0f6e 100644 --- a/mediagoblin/_version.py +++ b/mediagoblin/_version.py @@ -23,4 +23,4 @@ # see http://www.python.org/dev/peps/pep-0386/ -__version__ = "0.3.0-dev" +__version__ = "0.3.0.dev" diff --git a/mediagoblin/app.py b/mediagoblin/app.py index 06627675..0a57c091 100644 --- a/mediagoblin/app.py +++ b/mediagoblin/app.py @@ -16,11 +16,12 @@ import os import urllib +import logging import routes from webob import Request, exc -from mediagoblin import routing, meddleware +from mediagoblin import routing, meddleware, __version__ from mediagoblin.tools import common, translate, template from mediagoblin.tools.response import render_404 from mediagoblin.tools import request as mg_request @@ -31,6 +32,9 @@ from mediagoblin.init import (get_jinja_loader, get_staticdirector, setup_storage, setup_beaker_cache) +_log = logging.getLogger(__name__) + + class MediaGoblinApp(object): """ WSGI application of MediaGoblin @@ -47,6 +51,7 @@ class MediaGoblinApp(object): (Note: setting 'celery_setup_elsewhere' also disables setting up celery.) """ + _log.info("GNU MediaGoblin %s main server starting", __version__) ############## # Setup config ############## @@ -179,6 +184,14 @@ class MediaGoblinApp(object): for m in self.meddleware[::-1]: m.process_response(request, response) + # Reset the sql session, so that the next request + # gets a fresh session + try: + self.db.reset_after_request() + except TypeError: + # We're still on mongo + pass + return response(environ, start_response) diff --git a/mediagoblin/auth/lib.py b/mediagoblin/auth/lib.py index 1136a252..ddb58fe6 100644 --- a/mediagoblin/auth/lib.py +++ b/mediagoblin/auth/lib.py @@ -42,7 +42,7 @@ def bcrypt_check_password(raw_pass, stored_hash, extra_salt=None): if extra_salt: raw_pass = u"%s:%s" % (extra_salt, raw_pass) - hashed_pass = bcrypt.hashpw(raw_pass, stored_hash) + hashed_pass = bcrypt.hashpw(raw_pass.encode('utf-8'), stored_hash) # Reduce risk of timing attacks by hashing again with a random # number (thx to zooko on this advice, which I hopefully @@ -68,7 +68,8 @@ def bcrypt_gen_password_hash(raw_pass, extra_salt=None): if extra_salt: raw_pass = u"%s:%s" % (extra_salt, raw_pass) - return unicode(bcrypt.hashpw(raw_pass, bcrypt.gensalt())) + return unicode( + bcrypt.hashpw(raw_pass.encode('utf-8'), bcrypt.gensalt())) def fake_login_attempt(): diff --git a/mediagoblin/auth/views.py b/mediagoblin/auth/views.py index 9af89c2a..71a5f379 100644 --- a/mediagoblin/auth/views.py +++ b/mediagoblin/auth/views.py @@ -60,7 +60,9 @@ def register(request): if request.method == 'POST' and register_form.validate(): # TODO: Make sure the user doesn't exist already username = unicode(request.POST['username'].lower()) - email = unicode(request.POST['email'].lower()) + em_user, em_dom = unicode(request.POST['email']).split("@", 1) + em_dom = em_dom.lower() + email = em_user + "@" + em_dom users_with_username = request.db.User.find( {'username': username}).count() users_with_email = request.db.User.find( @@ -118,7 +120,7 @@ def login(request): login_failed = False if request.method == 'POST' and login_form.validate(): - user = request.db.User.one( + user = request.db.User.find_one( {'username': request.POST['username'].lower()}) if user and user.check_login(request.POST['password']): @@ -234,7 +236,8 @@ def forgot_password(request): Sends an email with an url to renew forgotten password """ - fp_form = auth_forms.ForgotPassForm(request.POST) + fp_form = auth_forms.ForgotPassForm(request.POST, + username=request.GET.get('username')) if request.method == 'POST' and fp_form.validate(): diff --git a/mediagoblin/config_spec.ini b/mediagoblin/config_spec.ini index 5c71a9b8..e30825de 100644 --- a/mediagoblin/config_spec.ini +++ b/mediagoblin/config_spec.ini @@ -2,6 +2,9 @@ # HTML title of the pages html_title = string(default="GNU MediaGoblin") +# link to source for this MediaGoblin site +source_link = string(default="https://gitorious.org/mediagoblin/mediagoblin") + # Enabled media types media_types = string_list(default=list("mediagoblin.media_types.image")) @@ -9,6 +12,7 @@ media_types = string_list(default=list("mediagoblin.media_types.image")) db_host = string() db_name = string(default="mediagoblin") db_port = integer() +sql_engine = string(default="sqlite:///%(here)s/mediagoblin.db") # Where temporary files used in processing and etc are kept workbench_path = string(default="%(here)s/user_dev/media/workbench") @@ -85,7 +89,7 @@ keep_original = boolean(default=False) [media_type:mediagoblin.media_types.audio] # vorbisenc qualiy quality = float(default=0.3) -create_spectrogram = boolean(default=False) +create_spectrogram = boolean(default=True) [beaker.cache] @@ -95,46 +99,54 @@ lock_dir = string(default="%(here)s/user_dev/beaker/cache/lock") [celery] +# default result stuff +CELERY_RESULT_BACKEND = string(default="database") +CELERY_RESULT_DBURI = string(default="sqlite:///%(here)s/celery.db") + +# default kombu stuff +BROKER_TRANSPORT = string(default="sqlalchemy") +BROKER_HOST = string(default="sqlite:///%(here)s/kombu.db") + # known booleans -celery_result_persistent = boolean() -celery_create_missing_queues = boolean() -broker_use_ssl = boolean() -broker_connection_retry = boolean() -celery_always_eager = boolean() -celery_eager_propagates_exceptions = boolean() -celery_ignore_result = boolean() -celery_track_started = boolean() -celery_disable_rate_limits = boolean() -celery_acks_late = boolean() -celery_store_errors_even_if_ignored = boolean() -celery_send_task_error_emails = boolean() -celery_send_events = boolean() -celery_send_task_sent_event = boolean() -celeryd_log_color = boolean() -celery_redirect_stdouts = boolean() +CELERY_RESULT_PERSISTENT = boolean() +CELERY_CREATE_MISSING_QUEUES = boolean() +BROKER_USE_SSL = boolean() +BROKER_CONNECTION_RETRY = boolean() +CELERY_ALWAYS_EAGER = boolean() +CELERY_EAGER_PROPAGATES_EXCEPTIONS = boolean() +CELERY_IGNORE_RESULT = boolean() +CELERY_TRACK_STARTED = boolean() +CELERY_DISABLE_RATE_LIMITS = boolean() +CELERY_ACKS_LATE = boolean() +CELERY_STORE_ERRORS_EVEN_IF_IGNORED = boolean() +CELERY_SEND_TASK_ERROR_EMAILS = boolean() +CELERY_SEND_EVENTS = boolean() +CELERY_SEND_TASK_SENT_EVENT = boolean() +CELERYD_LOG_COLOR = boolean() +CELERY_REDIRECT_STDOUTS = boolean() # known ints -celeryd_concurrency = integer() -celeryd_prefetch_multiplier = integer() -celery_amqp_task_result_expires = integer() -celery_amqp_task_result_connection_max = integer() -redis_port = integer() -redis_db = integer() -broker_port = integer() -broker_connection_timeout = integer() -celery_broker_connection_max_retries = integer() -celery_task_result_expires = integer() -celery_max_cached_results = integer() -celery_default_rate_limit = integer() -celeryd_max_tasks_per_child = integer() -celeryd_task_time_limit = integer() -celeryd_task_soft_time_limit = integer() -mail_port = integer() -celerybeat_max_loop_interval = integer() +CELERYD_CONCURRENCY = integer() +CELERYD_PREFETCH_MULTIPLIER = integer() +CELERY_AMQP_TASK_RESULT_EXPIRES = integer() +CELERY_AMQP_TASK_RESULT_CONNECTION_MAX = integer() +REDIS_PORT = integer() +REDIS_DB = integer() +BROKER_PORT = integer() +BROKER_CONNECTION_TIMEOUT = integer() +CELERY_BROKER_CONNECTION_MAX_RETRIES = integer() +CELERY_TASK_RESULT_EXPIRES = integer() +CELERY_MAX_CACHED_RESULTS = integer() +CELERY_DEFAULT_RATE_LIMIT = integer() +CELERYD_MAX_TASKS_PER_CHILD = integer() +CELERYD_TASK_TIME_LIMIT = integer() +CELERYD_TASK_SOFT_TIME_LIMIT = integer() +MAIL_PORT = integer() +CELERYBEAT_MAX_LOOP_INTERVAL = integer() # known floats -celeryd_eta_scheduler_precision = float() +CELERYD_ETA_SCHEDULER_PRECISION = float() # known lists -celery_routes = string_list() -celery_imports = string_list() +CELERY_ROUTES = string_list() +CELERY_IMPORTS = string_list() diff --git a/mediagoblin/db/mixin.py b/mediagoblin/db/mixin.py index beaff9b0..a5aded02 100644 --- a/mediagoblin/db/mixin.py +++ b/mediagoblin/db/mixin.py @@ -27,8 +27,11 @@ These functions now live here and get "mixed in" into the real objects. """ +from mediagoblin import mg_globals from mediagoblin.auth import lib as auth_lib from mediagoblin.tools import common, licenses +from mediagoblin.tools.text import cleaned_markdown_conversion +from mediagoblin.tools.url import slugify class UserMixin(object): @@ -39,8 +42,36 @@ class UserMixin(object): return auth_lib.bcrypt_check_password( password, self.pw_hash) + @property + def bio_html(self): + return cleaned_markdown_conversion(self.bio) + class MediaEntryMixin(object): + def generate_slug(self): + # import this here due to a cyclic import issue + # (db.models -> db.mixin -> db.util -> db.models) + from mediagoblin.db.util import check_media_slug_used + + self.slug = slugify(self.title) + + duplicate = check_media_slug_used(mg_globals.database, + self.uploader, self.slug, self.id) + + if duplicate: + if self.id is not None: + self.slug = "%s-%s" % (self.id, self.slug) + else: + self.slug = None + + @property + def description_html(self): + """ + Rendered version of the description, run through + Markdown and cleaned with our cleaning tool. + """ + return cleaned_markdown_conversion(self.description) + def get_display_media(self, media_map, fetch_order=common.DISPLAY_IMAGE_FETCHING_ORDER): """ @@ -91,3 +122,24 @@ class MediaEntryMixin(object): def get_license_data(self): """Return license dict for requested license""" return licenses.SUPPORTED_LICENSES[self.license or ""] + + def exif_display_iter(self): + from mediagoblin.tools.exif import USEFUL_TAGS + + if not self.media_data: + return + exif_all = self.media_data.get("exif_all") + + for key in USEFUL_TAGS: + if key in exif_all: + yield key, exif_all[key] + + +class MediaCommentMixin(object): + @property + def content_html(self): + """ + the actual html-rendered version of the comment displayed. + Run through Markdown and the HTML cleaner. + """ + return cleaned_markdown_conversion(self.content) diff --git a/mediagoblin/db/mongo/migrations.py b/mediagoblin/db/mongo/migrations.py index 261e21a5..732f5846 100644 --- a/mediagoblin/db/mongo/migrations.py +++ b/mediagoblin/db/mongo/migrations.py @@ -29,6 +29,16 @@ def add_table_field(db, table_name, field_name, default_value): multi=True) +def drop_table_field(db, table_name, field_name): + """ + Drop an old field from a table/collection + """ + db[table_name].update( + {field_name: {'$exists': True}}, + {'$unset': {field_name: 1}}, + multi=True) + + # Please see mediagoblin/tests/test_migrations.py for some examples of # basic migrations. @@ -109,9 +119,82 @@ def media_type_image_to_multimedia_type_image(database): {'$set': {'media_type': 'mediagoblin.media_types.image'}}, multi=True) + @RegisterMigration(8) def mediaentry_add_license(database): """ Add the 'license' field for entries that don't have it. """ add_table_field(database, 'media_entries', 'license', None) + + +@RegisterMigration(9) +def remove_calculated_html(database): + """ + Drop pre-rendered html again and calculate things + on the fly (and cache): + - User.bio_html + - MediaEntry.description_html + - MediaComment.content_html + """ + drop_table_field(database, 'users', 'bio_html') + drop_table_field(database, 'media_entries', 'description_html') + drop_table_field(database, 'media_comments', 'content_html') + + +@RegisterMigration(10) +def convert_video_media_data(database): + """ + Move media_data["video"] directly into media_data + """ + collection = database['media_entries'] + target = collection.find( + {'media_data.video': {'$exists': True}}) + + for document in target: + assert len(document['media_data']) == 1 + document['media_data'] = document['media_data']['video'] + collection.save(document) + + +@RegisterMigration(11) +def convert_gps_media_data(database): + """ + Move media_data["gps"]["*"] to media_data["gps_*"]. + In preparation for media_data.gps_* + """ + collection = database['media_entries'] + target = collection.find( + {'media_data.gps': {'$exists': True}}) + + for document in target: + for key, value in document['media_data']['gps'].iteritems(): + document['media_data']['gps_' + key] = value + del document['media_data']['gps'] + collection.save(document) + + +@RegisterMigration(12) +def convert_exif_media_data(database): + """ + Move media_data["exif"]["clean"] to media_data["exif_all"]. + Drop media_data["exif"]["useful"] + In preparation for media_data.exif_all + """ + collection = database['media_entries'] + target = collection.find( + {'media_data.exif.clean': {'$exists': True}}) + + for document in target: + media_data = document['media_data'] + + exif_all = media_data['exif'].pop('clean') + if len(exif_all): + media_data['exif_all'] = exif_all + + del media_data['exif']['useful'] + + assert len(media_data['exif']) == 0 + del media_data['exif'] + + collection.save(document) diff --git a/mediagoblin/db/mongo/models.py b/mediagoblin/db/mongo/models.py index 541086bc..2e35a2b8 100644 --- a/mediagoblin/db/mongo/models.py +++ b/mediagoblin/db/mongo/models.py @@ -18,12 +18,21 @@ import datetime from mongokit import Document -from mediagoblin import mg_globals from mediagoblin.db.mongo import migrations from mediagoblin.db.mongo.util import ASCENDING, DESCENDING, ObjectId from mediagoblin.tools.pagination import Pagination -from mediagoblin.tools import url -from mediagoblin.db.mixin import UserMixin, MediaEntryMixin +from mediagoblin.db.mixin import UserMixin, MediaEntryMixin, MediaCommentMixin + + +class MongoPK(object): + """An alias for the _id primary key""" + def __get__(self, instance, cls): + return instance['_id'] + def __set__(self, instance, val): + instance['_id'] = val + def __delete__(self, instance): + del instance['_id'] + ################### # Custom validators @@ -59,7 +68,6 @@ class User(Document, UserMixin): - is_admin: Whether or not this user is an administrator or not. - url: this user's personal webpage/website, if appropriate. - bio: biography of this user (plaintext, in markdown) - - bio_html: biography of the user converted to proper HTML. """ __collection__ = 'users' use_dot_notation = True @@ -76,7 +84,6 @@ class User(Document, UserMixin): 'is_admin': bool, 'url': unicode, 'bio': unicode, # May contain markdown - 'bio_html': unicode, # May contain plaintext, or HTML 'fp_verification_key': unicode, # forgotten password verification key 'fp_token_expire': datetime.datetime, } @@ -89,6 +96,8 @@ class User(Document, UserMixin): 'status': u'needs_email_verification', 'is_admin': False} + id = MongoPK() + class MediaEntry(Document, MediaEntryMixin): """ @@ -112,9 +121,6 @@ class MediaEntry(Document, MediaEntryMixin): up with MarkDown for slight fanciness (links, boldness, italics, paragraphs...) - - description_html: Rendered version of the description, run through - Markdown and cleaned with our cleaning tool. - - media_type: What type of media is this? Currently we only support 'image' ;) @@ -179,7 +185,6 @@ class MediaEntry(Document, MediaEntryMixin): 'slug': unicode, 'created': datetime.datetime, 'description': unicode, # May contain markdown/up - 'description_html': unicode, # May contain plaintext, or HTML 'media_type': unicode, 'media_data': dict, # extra data relevant to this media_type 'plugin_data': dict, # plugins can dump stuff here. @@ -211,6 +216,11 @@ class MediaEntry(Document, MediaEntryMixin): 'created': datetime.datetime.utcnow, 'state': u'unprocessed'} + id = MongoPK() + + def media_data_init(self, **kwargs): + self.media_data.update(kwargs) + def get_comments(self, ascending=False): if ascending: order = ASCENDING @@ -220,15 +230,6 @@ class MediaEntry(Document, MediaEntryMixin): return self.db.MediaComment.find({ 'media_entry': self._id}).sort('created', order) - def generate_slug(self): - self.slug = url.slugify(self.title) - - duplicate = mg_globals.database.media_entries.find_one( - {'slug': self.slug}) - - if duplicate: - self.slug = "%s-%s" % (self._id, self.slug) - def url_to_prev(self, urlgen): """ Provide a url to the previous entry from this user, if there is one @@ -257,7 +258,7 @@ class MediaEntry(Document, MediaEntryMixin): return self.db.User.find_one({'_id': self.uploader}) -class MediaComment(Document): +class MediaComment(Document, MediaCommentMixin): """ A comment on a MediaEntry. @@ -266,8 +267,6 @@ class MediaComment(Document): - author: user who posted this comment - created: when the comment was created - content: plaintext (but markdown'able) version of the comment's content. - - content_html: the actual html-rendered version of the comment displayed. - Run through Markdown and the HTML cleaner. """ __collection__ = 'media_comments' @@ -278,7 +277,7 @@ class MediaComment(Document): 'author': ObjectId, 'created': datetime.datetime, 'content': unicode, - 'content_html': unicode} + } required_fields = [ 'media_entry', 'author', 'created', 'content'] diff --git a/mediagoblin/db/mongo/open.py b/mediagoblin/db/mongo/open.py index bedc497b..c4f37b42 100644 --- a/mediagoblin/db/mongo/open.py +++ b/mediagoblin/db/mongo/open.py @@ -21,6 +21,10 @@ from mediagoblin.db.mongo import models from mediagoblin.db.mongo.util import MigrationManager +def load_models(app_config): + pass + + def connect_database_from_config(app_config, use_pymongo=False): """ Connect to the main database, take config from app_config diff --git a/mediagoblin/db/mongo/util.py b/mediagoblin/db/mongo/util.py index 4daf616a..f61ae6be 100644 --- a/mediagoblin/db/mongo/util.py +++ b/mediagoblin/db/mongo/util.py @@ -290,3 +290,29 @@ class MigrationManager(object): self.set_current_migration(migration_number) if post_callback: post_callback(migration_number, migration_func) + + +########################## +# Random utility functions +########################## + + +def atomic_update(table, query_dict, update_values): + table.collection.update( + query_dict, + {"$set": update_values}) + + +def check_media_slug_used(db, uploader_id, slug, ignore_m_id): + query_dict = {'uploader': uploader_id, 'slug': slug} + if ignore_m_id is not None: + query_dict['_id'] = {'$ne': ignore_m_id} + existing_user_slug_entries = db.MediaEntry.find( + query_dict).count() + return existing_user_slug_entries + + +def media_entries_for_tag_slug(db, tag_slug): + return db.MediaEntry.find( + {u'state': u'processed', + u'tags.slug': tag_slug}) diff --git a/mediagoblin/db/open.py b/mediagoblin/db/open.py index 0163469f..f4c38511 100644 --- a/mediagoblin/db/open.py +++ b/mediagoblin/db/open.py @@ -21,7 +21,9 @@ except ImportError: if use_sql: from mediagoblin.db.sql.open import \ - setup_connection_and_db_from_config, check_db_migrations_current + setup_connection_and_db_from_config, check_db_migrations_current, \ + load_models else: from mediagoblin.db.mongo.open import \ - setup_connection_and_db_from_config, check_db_migrations_current + setup_connection_and_db_from_config, check_db_migrations_current, \ + load_models diff --git a/mediagoblin/db/sql/base.py b/mediagoblin/db/sql/base.py index 6ed24a03..838080b0 100644 --- a/mediagoblin/db/sql/base.py +++ b/mediagoblin/db/sql/base.py @@ -67,6 +67,10 @@ class GMGTableBase(object): def get(self, key): return getattr(self, key) + def setdefault(self, key, defaultvalue): + # The key *has* to exist on sql. + return getattr(self, key) + def save(self, validate=True): assert validate sess = object_session(self) @@ -75,6 +79,12 @@ class GMGTableBase(object): sess.add(self) sess.commit() + def delete(self): + sess = object_session(self) + assert sess is not None, "Not going to delete detached %r" % self + sess.delete(self) + sess.commit() + Base = declarative_base(cls=GMGTableBase) diff --git a/mediagoblin/db/sql/convert.py b/mediagoblin/db/sql/convert.py index f6575be9..ebf3037c 100644 --- a/mediagoblin/db/sql/convert.py +++ b/mediagoblin/db/sql/convert.py @@ -14,17 +14,20 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. +from copy import copy from mediagoblin.init import setup_global_and_app_config, setup_database from mediagoblin.db.mongo.util import ObjectId -from mediagoblin.db.sql.models import (Base, User, MediaEntry, MediaComment, - Tag, MediaTag, MediaFile) +from mediagoblin.db.sql.base import Base, Session +from mediagoblin.db.sql.models import (User, MediaEntry, MediaComment, + Tag, MediaTag, MediaFile, MediaAttachmentFile, MigrationData) +from mediagoblin.media_types.image.models import ImageData +from mediagoblin.media_types.video.models import VideoData from mediagoblin.db.sql.open import setup_connection_and_db_from_config as \ sql_connect from mediagoblin.db.mongo.open import setup_connection_and_db_from_config as \ mongo_connect -from mediagoblin.db.sql.base import Session obj_id_table = dict() @@ -49,14 +52,14 @@ def copy_reference_attr(entry, new_entry, ref_attr): def convert_users(mk_db): session = Session() - for entry in mk_db.User.find(): + for entry in mk_db.User.find().sort('created'): print entry.username new_entry = User() copy_attrs(entry, new_entry, ('username', 'email', 'created', 'pw_hash', 'email_verified', 'status', 'verification_key', 'is_admin', 'url', - 'bio', 'bio_html', + 'bio', 'fp_verification_key', 'fp_token_expire',)) # new_entry.fp_verification_expire = entry.fp_token_expire @@ -71,15 +74,15 @@ def convert_users(mk_db): def convert_media_entries(mk_db): session = Session() - for entry in mk_db.MediaEntry.find(): + for entry in mk_db.MediaEntry.find().sort('created'): print repr(entry.title) new_entry = MediaEntry() copy_attrs(entry, new_entry, ('title', 'slug', 'created', - 'description', 'description_html', + 'description', 'media_type', 'state', 'license', - 'fail_error', + 'fail_error', 'fail_metadata', 'queued_task_id',)) copy_reference_attr(entry, new_entry, "uploader") @@ -92,6 +95,44 @@ def convert_media_entries(mk_db): new_file.media_entry = new_entry.id Session.add(new_file) + for attachment in entry.attachment_files: + new_attach = MediaAttachmentFile( + name=attachment["name"], + filepath=attachment["filepath"], + created=attachment["created"] + ) + new_attach.media_entry = new_entry.id + Session.add(new_attach) + + session.commit() + session.close() + + +def convert_image(mk_db): + session = Session() + + for media in mk_db.MediaEntry.find( + {'media_type': 'mediagoblin.media_types.image'}).sort('created'): + media_data = copy(media.media_data) + + if len(media_data): + media_data_row = ImageData(**media_data) + media_data_row.media_entry = obj_id_table[media['_id']] + session.add(media_data_row) + + session.commit() + session.close() + + +def convert_video(mk_db): + session = Session() + + for media in mk_db.MediaEntry.find( + {'media_type': 'mediagoblin.media_types.video'}).sort('created'): + media_data_row = VideoData(**media.media_data) + media_data_row.media_entry = obj_id_table[media['_id']] + session.add(media_data_row) + session.commit() session.close() @@ -100,7 +141,7 @@ def convert_media_tags(mk_db): session = Session() session.autoflush = False - for media in mk_db.MediaEntry.find(): + for media in mk_db.MediaEntry.find().sort('created'): print repr(media.title) for otag in media.tags: @@ -127,13 +168,13 @@ def convert_media_tags(mk_db): def convert_media_comments(mk_db): session = Session() - for entry in mk_db.MediaComment.find(): + for entry in mk_db.MediaComment.find().sort('created'): print repr(entry.content) new_entry = MediaComment() copy_attrs(entry, new_entry, ('created', - 'content', 'content_html',)) + 'content',)) copy_reference_attr(entry, new_entry, "media_entry") copy_reference_attr(entry, new_entry, "author") @@ -145,11 +186,24 @@ def convert_media_comments(mk_db): session.close() -def main(): - global_config, app_config = setup_global_and_app_config("mediagoblin.ini") +def convert_add_migration_versions(): + session = Session() + + for name in ("__main__", + "mediagoblin.media_types.image", + "mediagoblin.media_types.video", + ): + m = MigrationData(name=name, version=0) + session.add(m) + + session.commit() + session.close() + - sql_conn, sql_db = sql_connect({'sql_engine': 'sqlite:///mediagoblin.db'}) +def run_conversion(config_name): + global_config, app_config = setup_global_and_app_config(config_name) + sql_conn, sql_db = sql_connect(app_config) mk_conn, mk_db = mongo_connect(app_config) Base.metadata.create_all(sql_db.engine) @@ -158,11 +212,17 @@ def main(): Session.remove() convert_media_entries(mk_db) Session.remove() + convert_image(mk_db) + Session.remove() + convert_video(mk_db) + Session.remove() convert_media_tags(mk_db) Session.remove() convert_media_comments(mk_db) Session.remove() + convert_add_migration_versions() + Session.remove() if __name__ == '__main__': - main() + run_conversion("mediagoblin.ini") diff --git a/mediagoblin/db/sql/extratypes.py b/mediagoblin/db/sql/extratypes.py index 3a594728..8e078f14 100644 --- a/mediagoblin/db/sql/extratypes.py +++ b/mediagoblin/db/sql/extratypes.py @@ -15,7 +15,8 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. -from sqlalchemy.types import TypeDecorator, Unicode +from sqlalchemy.types import TypeDecorator, Unicode, VARCHAR +import json class PathTupleWithSlashes(TypeDecorator): @@ -35,3 +36,28 @@ class PathTupleWithSlashes(TypeDecorator): if value is not None: value = tuple(value.split('/')) return value + + +# The following class and only this one class is in very +# large parts based on example code from sqlalchemy. +# +# The original copyright notice and license follows: +# Copyright (C) 2005-2011 the SQLAlchemy authors and contributors <see AUTHORS file> +# +# This module is part of SQLAlchemy and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php +# +class JSONEncoded(TypeDecorator): + "Represents an immutable structure as a json-encoded string." + + impl = VARCHAR + + def process_bind_param(self, value, dialect): + if value is not None: + value = json.dumps(value) + return value + + def process_result_value(self, value, dialect): + if value is not None: + value = json.loads(value) + return value diff --git a/mediagoblin/db/sql/migrations.py b/mediagoblin/db/sql/migrations.py new file mode 100644 index 00000000..98d0d0aa --- /dev/null +++ b/mediagoblin/db/sql/migrations.py @@ -0,0 +1,17 @@ +# GNU MediaGoblin -- federated, autonomous media hosting +# Copyright (C) 2011 MediaGoblin contributors. See AUTHORS. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +MIGRATIONS = {} diff --git a/mediagoblin/db/sql/models.py b/mediagoblin/db/sql/models.py index 36f94b25..e87aaddb 100644 --- a/mediagoblin/db/sql/models.py +++ b/mediagoblin/db/sql/models.py @@ -14,20 +14,34 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. +""" +TODO: indexes on foreignkeys, where useful. +""" + import datetime +import sys from sqlalchemy import ( Column, Integer, Unicode, UnicodeText, DateTime, Boolean, ForeignKey, - UniqueConstraint) + UniqueConstraint, PrimaryKeyConstraint, SmallInteger) from sqlalchemy.orm import relationship from sqlalchemy.orm.collections import attribute_mapped_collection from sqlalchemy.sql.expression import desc from sqlalchemy.ext.associationproxy import association_proxy +from sqlalchemy.util import memoized_property -from mediagoblin.db.sql.extratypes import PathTupleWithSlashes +from mediagoblin.db.sql.extratypes import PathTupleWithSlashes, JSONEncoded from mediagoblin.db.sql.base import Base, DictReadAttrProxy -from mediagoblin.db.mixin import UserMixin, MediaEntryMixin +from mediagoblin.db.mixin import UserMixin, MediaEntryMixin, MediaCommentMixin +from mediagoblin.db.sql.base import Session + +# It's actually kind of annoying how sqlalchemy-migrate does this, if +# I understand it right, but whatever. Anyway, don't remove this :P +# +# We could do migration calls more manually instead of relying on +# this import-based meddling... +from migrate import changeset class SimpleFieldAlias(object): @@ -43,7 +57,11 @@ class SimpleFieldAlias(object): class User(Base, UserMixin): - __tablename__ = "users" + """ + TODO: We should consider moving some rarely used fields + into some sort of "shadow" table. + """ + __tablename__ = "core__users" id = Column(Integer, primary_key=True) username = Column(Unicode, nullable=False, unique=True) @@ -56,7 +74,6 @@ class User(Base, UserMixin): is_admin = Column(Boolean, default=False, nullable=False) url = Column(Unicode) bio = Column(UnicodeText) # ?? - bio_html = Column(UnicodeText) # ?? fp_verification_key = Column(Unicode) fp_token_expire = Column(DateTime) @@ -67,22 +84,25 @@ class User(Base, UserMixin): class MediaEntry(Base, MediaEntryMixin): - __tablename__ = "media_entries" + """ + TODO: Consider fetching the media_files using join + """ + __tablename__ = "core__media_entries" id = Column(Integer, primary_key=True) - uploader = Column(Integer, ForeignKey('users.id'), nullable=False) + uploader = Column(Integer, ForeignKey(User.id), nullable=False, index=True) title = Column(Unicode, nullable=False) slug = Column(Unicode) - created = Column(DateTime, nullable=False, default=datetime.datetime.now) + created = Column(DateTime, nullable=False, default=datetime.datetime.now, + index=True) description = Column(UnicodeText) # ?? - description_html = Column(UnicodeText) # ?? media_type = Column(Unicode, nullable=False) state = Column(Unicode, default=u'unprocessed', nullable=False) # or use sqlalchemy.types.Enum? license = Column(Unicode) fail_error = Column(Unicode) - fail_metadata = Column(UnicodeText) + fail_metadata = Column(JSONEncoded) queued_media_file = Column(PathTupleWithSlashes) @@ -102,6 +122,15 @@ class MediaEntry(Base, MediaEntryMixin): creator=lambda k, v: MediaFile(name=k, file_path=v) ) + attachment_files_helper = relationship("MediaAttachmentFile", + cascade="all, delete-orphan", + order_by="MediaAttachmentFile.created" + ) + attachment_files = association_proxy("attachment_files_helper", "dict_view", + creator=lambda v: MediaAttachmentFile( + name=v["name"], filepath=v["filepath"]) + ) + tags_helper = relationship("MediaTag", cascade="all, delete-orphan" ) @@ -111,7 +140,6 @@ class MediaEntry(Base, MediaEntryMixin): ## TODO # media_data - # attachment_files # fail_error _id = SimpleFieldAlias("id") @@ -143,22 +171,107 @@ class MediaEntry(Base, MediaEntryMixin): if media is not None: return media.url_for_self(urlgen) + #@memoized_property + @property + def media_data(self): + session = Session() + + return session.query(self.media_data_table).filter_by( + media_entry=self.id).first() + + def media_data_init(self, **kwargs): + """ + Initialize or update the contents of a media entry's media_data row + """ + session = Session() + + media_data = session.query(self.media_data_table).filter_by( + media_entry=self.id).first() + + # No media data, so actually add a new one + if media_data is None: + media_data = self.media_data_table( + media_entry=self.id, + **kwargs) + session.add(media_data) + # Update old media data + else: + for field, value in kwargs.iteritems(): + setattr(media_data, field, value) + + @memoized_property + def media_data_table(self): + # TODO: memoize this + models_module = self.media_type + '.models' + __import__(models_module) + return sys.modules[models_module].DATA_MODEL + + +class FileKeynames(Base): + """ + keywords for various places. + currently the MediaFile keys + """ + __tablename__ = "core__file_keynames" + id = Column(Integer, primary_key=True) + name = Column(Unicode, unique=True) + + def __repr__(self): + return "<FileKeyname %r: %r>" % (self.id, self.name) + + @classmethod + def find_or_new(cls, name): + t = cls.query.filter_by(name=name).first() + if t is not None: + return t + return cls(name=name) + class MediaFile(Base): - __tablename__ = "mediafiles" + """ + TODO: Highly consider moving "name" into a new table. + TODO: Consider preloading said table in software + """ + __tablename__ = "core__mediafiles" media_entry = Column( Integer, ForeignKey(MediaEntry.id), - nullable=False, primary_key=True) - name = Column(Unicode, primary_key=True) + nullable=False) + name_id = Column(SmallInteger, ForeignKey(FileKeynames.id), nullable=False) file_path = Column(PathTupleWithSlashes) + __table_args__ = ( + PrimaryKeyConstraint('media_entry', 'name_id'), + {}) + def __repr__(self): return "<MediaFile %s: %r>" % (self.name, self.file_path) + name_helper = relationship(FileKeynames, lazy="joined", innerjoin=True) + name = association_proxy('name_helper', 'name', + creator=FileKeynames.find_or_new + ) + + +class MediaAttachmentFile(Base): + __tablename__ = "core__attachment_files" + + id = Column(Integer, primary_key=True) + media_entry = Column( + Integer, ForeignKey(MediaEntry.id), + nullable=False) + name = Column(Unicode, nullable=False) + filepath = Column(PathTupleWithSlashes) + created = Column(DateTime, nullable=False, default=datetime.datetime.now) + + @property + def dict_view(self): + """A dict like view on this object""" + return DictReadAttrProxy(self) + class Tag(Base): - __tablename__ = "tags" + __tablename__ = "core__tags" id = Column(Integer, primary_key=True) slug = Column(Unicode, nullable=False, unique=True) @@ -175,13 +288,13 @@ class Tag(Base): class MediaTag(Base): - __tablename__ = "media_tags" + __tablename__ = "core__media_tags" id = Column(Integer, primary_key=True) media_entry = Column( Integer, ForeignKey(MediaEntry.id), - nullable=False) - tag = Column(Integer, ForeignKey('tags.id'), nullable=False) + nullable=False, index=True) + tag = Column(Integer, ForeignKey(Tag.id), nullable=False, index=True) name = Column(Unicode) # created = Column(DateTime, nullable=False, default=datetime.datetime.now) @@ -194,10 +307,12 @@ class MediaTag(Base): creator=Tag.find_or_new ) - def __init__(self, name, slug): + def __init__(self, name=None, slug=None): Base.__init__(self) - self.name = name - self.tag_helper = Tag.find_or_new(slug) + if name is not None: + self.name = name + if slug is not None: + self.tag_helper = Tag.find_or_new(slug) @property def dict_view(self): @@ -205,28 +320,56 @@ class MediaTag(Base): return DictReadAttrProxy(self) -class MediaComment(Base): - __tablename__ = "media_comments" +class MediaComment(Base, MediaCommentMixin): + __tablename__ = "core__media_comments" id = Column(Integer, primary_key=True) media_entry = Column( - Integer, ForeignKey('media_entries.id'), nullable=False) - author = Column(Integer, ForeignKey('users.id'), nullable=False) + Integer, ForeignKey(MediaEntry.id), nullable=False, index=True) + author = Column(Integer, ForeignKey(User.id), nullable=False) created = Column(DateTime, nullable=False, default=datetime.datetime.now) content = Column(UnicodeText, nullable=False) - content_html = Column(UnicodeText) get_author = relationship(User) _id = SimpleFieldAlias("id") -def show_table_init(): +MODELS = [ + User, MediaEntry, Tag, MediaTag, MediaComment, MediaFile, FileKeynames, + MediaAttachmentFile] + + +###################################################### +# Special, migrations-tracking table +# +# Not listed in MODELS because this is special and not +# really migrated, but used for migrations (for now) +###################################################### + +class MigrationData(Base): + __tablename__ = "core__migrations" + + name = Column(Unicode, primary_key=True) + version = Column(Integer, nullable=False, default=0) + +###################################################### + + +def show_table_init(engine_uri): + if engine_uri is None: + engine_uri = 'sqlite:///:memory:' from sqlalchemy import create_engine - engine = create_engine('sqlite:///:memory:', echo=True) + engine = create_engine(engine_uri, echo=True) Base.metadata.create_all(engine) if __name__ == '__main__': - show_table_init() + from sys import argv + print repr(argv) + if len(argv) == 2: + uri = argv[1] + else: + uri = None + show_table_init(uri) diff --git a/mediagoblin/db/sql/open.py b/mediagoblin/db/sql/open.py index 1bfc5538..edbf0785 100644 --- a/mediagoblin/db/sql/open.py +++ b/mediagoblin/db/sql/open.py @@ -16,9 +16,11 @@ from sqlalchemy import create_engine +import logging -from mediagoblin.db.sql.base import Session -from mediagoblin.db.sql.models import Base +from mediagoblin.db.sql.base import Base, Session + +_log = logging.getLogger(__name__) class DatabaseMaster(object): @@ -36,11 +38,22 @@ class DatabaseMaster(object): Session.flush() def reset_after_request(self): + Session.rollback() Session.remove() +def load_models(app_config): + import mediagoblin.db.sql.models + + if True: + for media_type in app_config['media_types']: + _log.debug("Loading %s.models", media_type) + __import__(media_type + ".models") + + def setup_connection_and_db_from_config(app_config): - engine = create_engine(app_config['sql_engine'], echo=True) + engine = create_engine(app_config['sql_engine']) + # logging.getLogger('sqlalchemy.engine').setLevel(logging.INFO) Session.configure(bind=engine) return "dummy conn", DatabaseMaster(engine) diff --git a/mediagoblin/db/sql/util.py b/mediagoblin/db/sql/util.py new file mode 100644 index 00000000..60024b28 --- /dev/null +++ b/mediagoblin/db/sql/util.py @@ -0,0 +1,324 @@ +# GNU MediaGoblin -- federated, autonomous media hosting +# Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + + +import sys +from mediagoblin.db.sql.base import Session +from mediagoblin.db.sql.models import MediaEntry, Tag, MediaTag + + +def _simple_printer(string): + """ + Prints a string, but without an auto \n at the end. + """ + sys.stdout.write(string) + sys.stdout.flush() + + +class MigrationManager(object): + """ + Migration handling tool. + + Takes information about a database, lets you update the database + to the latest migrations, etc. + """ + + def __init__(self, name, models, migration_registry, session, + printer=_simple_printer): + """ + Args: + - name: identifier of this section of the database + - session: session we're going to migrate + - migration_registry: where we should find all migrations to + run + """ + self.name = name + self.models = models + self.session = session + self.migration_registry = migration_registry + self._sorted_migrations = None + self.printer = printer + + # For convenience + from mediagoblin.db.sql.models import MigrationData + + self.migration_model = MigrationData + self.migration_table = MigrationData.__table__ + + @property + def sorted_migrations(self): + """ + Sort migrations if necessary and store in self._sorted_migrations + """ + if not self._sorted_migrations: + self._sorted_migrations = sorted( + self.migration_registry.items(), + # sort on the key... the migration number + key=lambda migration_tuple: migration_tuple[0]) + + return self._sorted_migrations + + @property + def migration_data(self): + """ + Get the migration row associated with this object, if any. + """ + return self.session.query( + self.migration_model).filter_by(name=self.name).first() + + @property + def latest_migration(self): + """ + Return a migration number for the latest migration, or 0 if + there are no migrations. + """ + if self.sorted_migrations: + return self.sorted_migrations[-1][0] + else: + # If no migrations have been set, we start at 0. + return 0 + + @property + def database_current_migration(self): + """ + Return the current migration in the database. + """ + # If the table doesn't even exist, return None. + if not self.migration_table.exists(self.session.bind): + return None + + # Also return None if self.migration_data is None. + if self.migration_data is None: + return None + + return self.migration_data.version + + def set_current_migration(self, migration_number=None): + """ + Set the migration in the database to migration_number + (or, the latest available) + """ + self.migration_data.version = migration_number or self.latest_migration + self.session.commit() + + def migrations_to_run(self): + """ + Get a list of migrations to run still, if any. + + Note that this will fail if there's no migration record for + this class! + """ + assert self.database_current_migration is not None + + db_current_migration = self.database_current_migration + + return [ + (migration_number, migration_func) + for migration_number, migration_func in self.sorted_migrations + if migration_number > db_current_migration] + + + def init_tables(self): + """ + Create all tables relative to this package + """ + # sanity check before we proceed, none of these should be created + for model in self.models: + # Maybe in the future just print out a "Yikes!" or something? + assert not model.__table__.exists(self.session.bind) + + self.migration_model.metadata.create_all( + self.session.bind, + tables=[model.__table__ for model in self.models]) + + def create_new_migration_record(self): + """ + Create a new migration record for this migration set + """ + migration_record = self.migration_model( + name=self.name, + version=self.latest_migration) + self.session.add(migration_record) + self.session.commit() + + def dry_run(self): + """ + Print out a dry run of what we would have upgraded. + """ + if self.database_current_migration is None: + self.printer( + u'~> Woulda initialized: %s\n' % self.name_for_printing()) + return u'inited' + + migrations_to_run = self.migrations_to_run() + if migrations_to_run: + self.printer( + u'~> Woulda updated %s:\n' % self.name_for_printing()) + + for migration_number, migration_func in migrations_to_run(): + self.printer( + u' + Would update %s, "%s"\n' % ( + migration_number, migration_func.func_name)) + + return u'migrated' + + def name_for_printing(self): + if self.name == u'__main__': + return u"main mediagoblin tables" + else: + # TODO: Use the friendlier media manager "human readable" name + return u'media type "%s"' % self.name + + def init_or_migrate(self): + """ + Initialize the database or migrate if appropriate. + + Returns information about whether or not we initialized + ('inited'), migrated ('migrated'), or did nothing (None) + """ + assure_migrations_table_setup(self.session) + + # Find out what migration number, if any, this database data is at, + # and what the latest is. + migration_number = self.database_current_migration + + # Is this our first time? Is there even a table entry for + # this identifier? + # If so: + # - create all tables + # - create record in migrations registry + # - print / inform the user + # - return 'inited' + if migration_number is None: + self.printer(u"-> Initializing %s... " % self.name_for_printing()) + + self.init_tables() + # auto-set at latest migration number + self.create_new_migration_record() + + self.printer(u"done.\n") + self.set_current_migration() + return u'inited' + + # Run migrations, if appropriate. + migrations_to_run = self.migrations_to_run() + if migrations_to_run: + self.printer( + u'-> Updating %s:\n' % self.name_for_printing()) + for migration_number, migration_func in migrations_to_run: + self.printer( + u' + Running migration %s, "%s"... ' % ( + migration_number, migration_func.func_name)) + migration_func(self.session) + self.printer('done.\n') + + self.set_current_migration() + return u'migrated' + + # Otherwise return None. Well it would do this anyway, but + # for clarity... ;) + return None + + +class RegisterMigration(object): + """ + Tool for registering migrations + + Call like: + + @RegisterMigration(33) + def update_dwarves(database): + [...] + + This will register your migration with the default migration + registry. Alternately, to specify a very specific + migration_registry, you can pass in that as the second argument. + + Note, the number of your migration should NEVER be 0 or less than + 0. 0 is the default "no migrations" state! + """ + def __init__(self, migration_number, migration_registry): + assert migration_number > 0, "Migration number must be > 0!" + assert migration_number not in migration_registry, \ + "Duplicate migration numbers detected! That's not allowed!" + + self.migration_number = migration_number + self.migration_registry = migration_registry + + def __call__(self, migration): + self.migration_registry[self.migration_number] = migration + return migration + + +def assure_migrations_table_setup(db): + """ + Make sure the migrations table is set up in the database. + """ + from mediagoblin.db.sql.models import MigrationData + + if not MigrationData.__table__.exists(db.bind): + MigrationData.metadata.create_all( + db.bind, tables=[MigrationData.__table__]) + + +########################## +# Random utility functions +########################## + + +def atomic_update(table, query_dict, update_values): + table.find(query_dict).update(update_values, + synchronize_session=False) + Session.commit() + + +def check_media_slug_used(dummy_db, uploader_id, slug, ignore_m_id): + filt = (MediaEntry.uploader == uploader_id) \ + & (MediaEntry.slug == slug) + if ignore_m_id is not None: + filt = filt & (MediaEntry.id != ignore_m_id) + does_exist = Session.query(MediaEntry.id).filter(filt).first() is not None + return does_exist + + +def media_entries_for_tag_slug(dummy_db, tag_slug): + return MediaEntry.query \ + .join(MediaEntry.tags_helper) \ + .join(MediaTag.tag_helper) \ + .filter( + (MediaEntry.state == u'processed') + & (Tag.slug == tag_slug)) + + +def clean_orphan_tags(): + q1 = Session.query(Tag).outerjoin(MediaTag).filter(MediaTag.id==None) + for t in q1: + Session.delete(t) + + # The "let the db do all the work" version: + # q1 = Session.query(Tag.id).outerjoin(MediaTag).filter(MediaTag.id==None) + # q2 = Session.query(Tag).filter(Tag.id.in_(q1)) + # q2.delete(synchronize_session = False) + + Session.commit() + + +if __name__ == '__main__': + from mediagoblin.db.sql.open import setup_connection_and_db_from_config + + conn,db = setup_connection_and_db_from_config({'sql_engine':'sqlite:///mediagoblin.db'}) + + clean_orphan_tags() diff --git a/mediagoblin/db/util.py b/mediagoblin/db/util.py index 1fc949a6..540a9244 100644 --- a/mediagoblin/db/util.py +++ b/mediagoblin/db/util.py @@ -21,5 +21,9 @@ except ImportError: if use_sql: from mediagoblin.db.sql.fake import ObjectId, InvalidId, DESCENDING + from mediagoblin.db.sql.util import atomic_update, check_media_slug_used, \ + media_entries_for_tag_slug else: - from mediagoblin.db.mongo.util import ObjectId, InvalidId, DESCENDING + from mediagoblin.db.mongo.util import \ + ObjectId, InvalidId, DESCENDING, atomic_update, \ + check_media_slug_used, media_entries_for_tag_slug diff --git a/mediagoblin/edit/views.py b/mediagoblin/edit/views.py index a7245517..2bcb5694 100644 --- a/mediagoblin/edit/views.py +++ b/mediagoblin/edit/views.py @@ -34,8 +34,9 @@ from mediagoblin.tools.response import render_to_response, redirect from mediagoblin.tools.translate import pass_to_ugettext as _ from mediagoblin.tools.text import ( clean_html, convert_to_tag_list_of_dicts, - media_tags_as_string, cleaned_markdown_conversion) + media_tags_as_string) from mediagoblin.tools.licenses import SUPPORTED_LICENSES +from mediagoblin.db.util import check_media_slug_used @get_user_media_entry @@ -58,12 +59,10 @@ def edit_media(request, media): if request.method == 'POST' and form.validate(): # Make sure there isn't already a MediaEntry with such a slug # and userid. - existing_user_slug_entries = request.db.MediaEntry.find( - {'slug': request.POST['slug'], - 'uploader': media.uploader, - '_id': {'$ne': media._id}}).count() + slug_used = check_media_slug_used(request.db, media.uploader, + request.POST['slug'], media.id) - if existing_user_slug_entries: + if slug_used: form.slug.errors.append( _(u'An entry with that slug already exists for this user.')) else: @@ -72,9 +71,6 @@ def edit_media(request, media): media.tags = convert_to_tag_list_of_dicts( request.POST.get('tags')) - media.description_html = cleaned_markdown_conversion( - media.description) - media.license = unicode(request.POST.get('license', '')) or None media.slug = unicode(request.POST['slug']) @@ -123,7 +119,7 @@ def edit_attachments(request, media): finally: request.POST['attachment_file'].file.close() - media['attachment_files'].append(dict( + media.attachment_files.append(dict( name=request.POST['attachment_name'] \ or request.POST['attachment_file'].filename, filepath=attachment_public_filepath, @@ -171,8 +167,6 @@ def edit_profile(request): user.url = unicode(request.POST['url']) user.bio = unicode(request.POST['bio']) - user.bio_html = cleaned_markdown_conversion(user.bio) - user.save() messages.add_message(request, @@ -180,7 +174,7 @@ def edit_profile(request): _("Profile changes saved")) return redirect(request, 'mediagoblin.user_pages.user_home', - user=user['username']) + user=user.username) return render_to_response( request, diff --git a/mediagoblin/gmg_commands/__init__.py b/mediagoblin/gmg_commands/__init__.py index db944b3c..054e2616 100644 --- a/mediagoblin/gmg_commands/__init__.py +++ b/mediagoblin/gmg_commands/__init__.py @@ -53,6 +53,14 @@ SUBCOMMAND_MAP = { 'setup': 'mediagoblin.gmg_commands.import_export:import_export_parse_setup', 'func': 'mediagoblin.gmg_commands.import_export:env_import', 'help': 'Exports the data for this MediaGoblin instance'}, + 'dbupdate': { + 'setup': 'mediagoblin.gmg_commands.dbupdate:dbupdate_parse_setup', + 'func': 'mediagoblin.gmg_commands.dbupdate:dbupdate', + 'help': 'Set up or update the SQL database'}, + 'convert_mongo_to_sql': { + 'setup': 'mediagoblin.gmg_commands.mongosql:mongosql_parser_setup', + 'func': 'mediagoblin.gmg_commands.mongosql:mongosql', + 'help': 'Convert Mongo DB data to SQL DB data'}, } diff --git a/mediagoblin/gmg_commands/dbupdate.py b/mediagoblin/gmg_commands/dbupdate.py new file mode 100644 index 00000000..27698170 --- /dev/null +++ b/mediagoblin/gmg_commands/dbupdate.py @@ -0,0 +1,89 @@ +# GNU MediaGoblin -- federated, autonomous media hosting +# Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +from sqlalchemy.orm import sessionmaker + +from mediagoblin.db.sql.open import setup_connection_and_db_from_config +from mediagoblin.db.sql.util import ( + MigrationManager, assure_migrations_table_setup) +from mediagoblin.init import setup_global_and_app_config +from mediagoblin.tools.common import import_component + + +def dbupdate_parse_setup(subparser): + pass + + +class DatabaseData(object): + def __init__(self, name, models, migrations): + self.name = name + self.models = models + self.migrations = migrations + + def make_migration_manager(self, session): + return MigrationManager( + self.name, self.models, self.migrations, session) + + +def gather_database_data(media_types): + """ + Gather all database data relevant to the extensions we have + installed so we can do migrations and table initialization. + + Returns a list of DatabaseData objects. + """ + managed_dbdata = [] + + # Add main first + from mediagoblin.db.sql.models import MODELS as MAIN_MODELS + from mediagoblin.db.sql.migrations import MIGRATIONS as MAIN_MIGRATIONS + + managed_dbdata.append( + DatabaseData( + '__main__', MAIN_MODELS, MAIN_MIGRATIONS)) + + # Then get all registered media managers (eventually, plugins) + for media_type in media_types: + models = import_component('%s.models:MODELS' % media_type) + migrations = import_component('%s.migrations:MIGRATIONS' % media_type) + managed_dbdata.append( + DatabaseData(media_type, models, migrations)) + + return managed_dbdata + + +def dbupdate(args): + """ + Initialize or migrate the database as specified by the config file. + + Will also initialize or migrate all extensions (media types, and + in the future, plugins) + """ + globa_config, app_config = setup_global_and_app_config(args.conf_file) + + # Gather information from all media managers / projects + dbdatas = gather_database_data(app_config['media_types']) + + # Set up the database + connection, db = setup_connection_and_db_from_config(app_config) + + Session = sessionmaker(bind=db.engine) + + # Setup media managers for all dbdata, run init/migrate and print info + # For each component, create/migrate tables + for dbdata in dbdatas: + migration_manager = dbdata.make_migration_manager(Session()) + migration_manager.init_or_migrate() diff --git a/mediagoblin/gmg_commands/migrate.py b/mediagoblin/gmg_commands/migrate.py index cacf5d19..af541786 100644 --- a/mediagoblin/gmg_commands/migrate.py +++ b/mediagoblin/gmg_commands/migrate.py @@ -17,7 +17,7 @@ import sys from mediagoblin.db.mongo import util as db_util -from mediagoblin.db.open import setup_connection_and_db_from_config +from mediagoblin.db.mongo.open import setup_connection_and_db_from_config from mediagoblin.init import setup_global_and_app_config # This MUST be imported so as to set up the appropriate migrations! @@ -41,7 +41,12 @@ def _print_finished_migration(migration_number, migration_func): def migrate(args): - global_config, app_config = setup_global_and_app_config(args.conf_file) + run_migrate(args.conf_file) + + +def run_migrate(conf_file): + global_config, app_config = setup_global_and_app_config(conf_file) + connection, db = setup_connection_and_db_from_config( app_config, use_pymongo=True) migration_manager = db_util.MigrationManager(db) diff --git a/mediagoblin/gmg_commands/mongosql.py b/mediagoblin/gmg_commands/mongosql.py new file mode 100644 index 00000000..dd53f575 --- /dev/null +++ b/mediagoblin/gmg_commands/mongosql.py @@ -0,0 +1,28 @@ +# GNU MediaGoblin -- federated, autonomous media hosting +# Copyright (C) 2012 MediaGoblin contributors. See AUTHORS. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + + +def mongosql_parser_setup(subparser): + pass + + +def mongosql(args): + # First, make sure our mongo migrations are up to date... + from mediagoblin.gmg_commands.migrate import run_migrate + run_migrate(args.conf_file) + + from mediagoblin.db.sql.convert import run_conversion + run_conversion(args.conf_file) diff --git a/mediagoblin/gmg_commands/shell.py b/mediagoblin/gmg_commands/shell.py index fe15e9f7..ec1ab535 100644 --- a/mediagoblin/gmg_commands/shell.py +++ b/mediagoblin/gmg_commands/shell.py @@ -22,7 +22,9 @@ from mediagoblin.gmg_commands import util as commands_util def shell_parser_setup(subparser): - pass + subparser.add_argument( + '--ipython', help='Use ipython', + action="store_true") SHELL_BANNER = """\ @@ -34,16 +36,42 @@ Available vars: - db: database instance """ +def py_shell(**user_namespace): + """ + Run a shell using normal python shell. + """ + code.interact( + banner=SHELL_BANNER, + local=user_namespace) + + +def ipython_shell(**user_namespace): + """ + Run a shell for the user using ipython. + """ + try: + from IPython import embed + except: + print "IPython not available... exiting!" + return + + embed( + banner1=SHELL_BANNER, + user_ns=user_namespace) + def shell(args): """ Setup a shell for the user + either a normal Python shell + or an IPython one """ - mgoblin_app = commands_util.setup_app(args) + user_namespace = { + 'mg_globals': mg_globals, + 'mgoblin_app': commands_util.setup_app(args), + 'db': mg_globals.database} - code.interact( - banner=SHELL_BANNER, - local={ - 'mgoblin_app': mgoblin_app, - 'mg_globals': mg_globals, - 'db': mg_globals.database}) + if args.ipython: + ipython_shell(**user_namespace) + else: + py_shell(**user_namespace) diff --git a/mediagoblin/gmg_commands/wipealldata.py b/mediagoblin/gmg_commands/wipealldata.py index 3081bbc0..37217fd1 100644 --- a/mediagoblin/gmg_commands/wipealldata.py +++ b/mediagoblin/gmg_commands/wipealldata.py @@ -20,12 +20,16 @@ import sys import os import shutil +from mediagoblin.init import setup_global_and_app_config + def wipe_parser_setup(subparser): pass def wipe(args): + global_config, app_config = setup_global_and_app_config(args.conf_file) + print "*** WARNING! ***" print "" print "Running this will destroy your mediagoblin database," @@ -39,12 +43,12 @@ def wipe(args): 'Are you **SURE** you want to destroy your environment? ' '(if so, type "yes")> ') - if not drop_it == 'yes': + if drop_it != 'yes': return print "nixing data in mongodb...." conn = pymongo.Connection() - conn.drop_database('mediagoblin') + conn.drop_database(app_config["db_name"]) for directory in [os.path.join(os.getcwd(), "user_dev", "media"), os.path.join(os.getcwd(), "user_dev", "beaker")]: diff --git a/mediagoblin/i18n/ar/LC_MESSAGES/mediagoblin.mo b/mediagoblin/i18n/ar/LC_MESSAGES/mediagoblin.mo Binary files differindex b23b0be4..ae932eaa 100644 --- a/mediagoblin/i18n/ar/LC_MESSAGES/mediagoblin.mo +++ b/mediagoblin/i18n/ar/LC_MESSAGES/mediagoblin.mo diff --git a/mediagoblin/i18n/ar/LC_MESSAGES/mediagoblin.po b/mediagoblin/i18n/ar/LC_MESSAGES/mediagoblin.po index 578be678..044968be 100644 --- a/mediagoblin/i18n/ar/LC_MESSAGES/mediagoblin.po +++ b/mediagoblin/i18n/ar/LC_MESSAGES/mediagoblin.po @@ -9,9 +9,9 @@ msgid "" msgstr "" "Project-Id-Version: GNU MediaGoblin\n" -"Report-Msgid-Bugs-To: http://bugs.foocorp.net/projects/mediagoblin/issues\n" -"POT-Creation-Date: 2012-01-29 13:31-0600\n" -"PO-Revision-Date: 2012-01-29 19:29+0000\n" +"Report-Msgid-Bugs-To: http://issues.mediagoblin.org/\n" +"POT-Creation-Date: 2012-03-18 15:16-0500\n" +"PO-Revision-Date: 2012-03-18 20:14+0000\n" "Last-Translator: cwebber <cwebber@dustycloud.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n" "MIME-Version: 1.0\n" @@ -21,7 +21,7 @@ msgstr "" "Language: ar\n" "Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5\n" -#: mediagoblin/processing.py:143 +#: mediagoblin/processing.py:154 msgid "Invalid file given for media type." msgstr "" @@ -41,56 +41,52 @@ msgstr "عنوان البريد الإلكتروني" msgid "Sorry, registration is disabled on this instance." msgstr "عÙوًا، التسجيل غير Ù…ØªØ§Ø Ù‡Ù†Ø§." -#: mediagoblin/auth/views.py:73 +#: mediagoblin/auth/views.py:75 msgid "Sorry, a user with that name already exists." msgstr "عذرًا، لقد اختار مستخدم آخر هذا الاسم." -#: mediagoblin/auth/views.py:77 +#: mediagoblin/auth/views.py:79 msgid "Sorry, a user with that email address already exists." msgstr "" -#: mediagoblin/auth/views.py:180 +#: mediagoblin/auth/views.py:182 msgid "" "Your email address has been verified. You may now login, edit your profile, " "and submit images!" -msgstr "" -"تم التØÙ‚Ù‚ من بريدك الإلكتروني. يمكنك الآن الولوج، ÙˆØªØØ±ÙŠØ± ملÙÙƒ الشخصي، ونشر " -"الصور!" +msgstr "تم التØÙ‚Ù‚ من بريدك الإلكتروني. يمكنك الآن الولوج، ÙˆØªØØ±ÙŠØ± ملÙÙƒ الشخصي، ونشر الصور!" -#: mediagoblin/auth/views.py:186 +#: mediagoblin/auth/views.py:188 msgid "The verification key or user id is incorrect" msgstr "Ù…ÙØªØ§Ø التØÙ‚Ù‚ أو معر٠المستخدم خاطئ" -#: mediagoblin/auth/views.py:204 +#: mediagoblin/auth/views.py:206 msgid "You must be logged in so we know who to send the email to!" msgstr "" -#: mediagoblin/auth/views.py:212 +#: mediagoblin/auth/views.py:214 msgid "You've already verified your email address!" msgstr "" -#: mediagoblin/auth/views.py:225 +#: mediagoblin/auth/views.py:227 msgid "Resent your verification email." msgstr "أعدنا إرسال رسالة التØÙ‚Ù‚." -#: mediagoblin/auth/views.py:260 +#: mediagoblin/auth/views.py:262 msgid "" "An email has been sent with instructions on how to change your password." msgstr "" -#: mediagoblin/auth/views.py:270 +#: mediagoblin/auth/views.py:272 msgid "" "Could not send password recovery email as your username is inactive or your " "account's email address has not been verified." -msgstr "" -"تعذر إرسال رسالة استعادة كلمة السر لأن اسم المستخدم معطل أو لأننا لم نتØÙ‚Ù‚ " -"من بريدك الإلكتروني." +msgstr "تعذر إرسال رسالة استعادة كلمة السر لأن اسم المستخدم معطل أو لأننا لم نتØÙ‚Ù‚ من بريدك الإلكتروني." -#: mediagoblin/auth/views.py:282 +#: mediagoblin/auth/views.py:284 msgid "Couldn't find someone with that username or email." msgstr "" -#: mediagoblin/auth/views.py:330 +#: mediagoblin/auth/views.py:332 msgid "You can now log in using your new password." msgstr "" @@ -133,6 +129,7 @@ msgid "" msgstr "" #: mediagoblin/edit/forms.py:44 mediagoblin/submit/forms.py:41 +#: mediagoblin/templates/mediagoblin/utils/license.html:20 msgid "License" msgstr "" @@ -156,27 +153,27 @@ msgstr "" msgid "New password" msgstr "" -#: mediagoblin/edit/views.py:68 +#: mediagoblin/edit/views.py:67 msgid "An entry with that slug already exists for this user." msgstr "يوجد مل٠آخر بهذا المسار لدى هذى المستخدم." -#: mediagoblin/edit/views.py:92 +#: mediagoblin/edit/views.py:88 msgid "You are editing another user's media. Proceed with caution." msgstr "أنت ØªØØ±Ù‘ر وسائط مستخدم آخر. كن ØØ°Ø±Ù‹Ø§ أثناء العملية." -#: mediagoblin/edit/views.py:162 +#: mediagoblin/edit/views.py:158 msgid "You are editing a user's profile. Proceed with caution." msgstr "أنت ØªØØ±Ù‘ر مل٠مستخدم آخر. كن ØØ°Ø±Ù‹Ø§ أثناء العملية." -#: mediagoblin/edit/views.py:180 +#: mediagoblin/edit/views.py:174 msgid "Profile changes saved" msgstr "" -#: mediagoblin/edit/views.py:206 +#: mediagoblin/edit/views.py:200 msgid "Wrong password" msgstr "" -#: mediagoblin/edit/views.py:222 +#: mediagoblin/edit/views.py:216 msgid "Account settings saved" msgstr "" @@ -196,7 +193,7 @@ msgstr "الملÙ" msgid "You must provide a file." msgstr "يجب أن تضع ملÙًا." -#: mediagoblin/submit/views.py:158 +#: mediagoblin/submit/views.py:156 msgid "Woohoo! Submitted!" msgstr "يا سلام! Ù†ÙØ´Ø±ÙŽØª!" @@ -216,8 +213,7 @@ msgstr "يبدو أنه لا توجد ØµÙØØ© ÙÙŠ العنوان. عذرًا!" msgid "" "If you're sure the address is correct, maybe the page you're looking for has" " been moved or deleted." -msgstr "" -"إن كنت متأكدًا من ØµØØ© العنوان ÙØ±Ø¨Ù…ا تكون Ø§Ù„ØµÙØØ© التي تريدها Ù†Ùقلت أو ØÙØ°ÙØª." +msgstr "إن كنت متأكدًا من ØµØØ© العنوان ÙØ±Ø¨Ù…ا تكون Ø§Ù„ØµÙØØ© التي تريدها Ù†Ùقلت أو ØÙØ°ÙØª." #: mediagoblin/templates/mediagoblin/base.html:46 msgid "MediaGoblin logo" @@ -245,7 +241,15 @@ msgstr "Ù„ÙØ¬" #: mediagoblin/templates/mediagoblin/base.html:84 msgid "" "Powered by <a href=\"http://mediagoblin.org\">MediaGoblin</a>, a <a " -"href=\"http://gnu.org/\">GNU</a> project" +"href=\"http://gnu.org/\">GNU</a> project." +msgstr "" + +#: mediagoblin/templates/mediagoblin/base.html:87 +#, python-format +msgid "" +"Released under the <a " +"href=\"http://www.fsf.org/licensing/licenses/agpl-3.0.html\">AGPL</a>. <a " +"href=\"%(source_link)s\">Source code</a> available." msgstr "" #: mediagoblin/templates/mediagoblin/root.html:24 @@ -312,14 +316,7 @@ msgid "" "\n" "If you think this is an error, just ignore this email and continue being\n" "a happy goblin!" -msgstr "" -"Ù…Ø±ØØ¨Ù‹Ø§ يا %(username)sØŒ\n" -"\n" -"إن أردت تغيير كلمة سرك ÙÙŠ غنو ميدياغوبلن ÙØ§ÙØªØ Ø§Ù„ÙˆØµÙ„Ø© التالية ÙÙŠ Ù…ØªØµÙØÙƒ:\n" -"\n" -"%(verification_url)s\n" -"\n" -"إن كنت ترى أن هذه الرسالة وصلتك خطأً ÙØªØ¬Ø§Ù‡Ù„ها واستمتع بØÙŠØ§ØªÙƒ!" +msgstr "Ù…Ø±ØØ¨Ù‹Ø§ يا %(username)sØŒ\n\nإن أردت تغيير كلمة سرك ÙÙŠ غنو ميدياغوبلن ÙØ§ÙØªØ Ø§Ù„ÙˆØµÙ„Ø© التالية ÙÙŠ Ù…ØªØµÙØÙƒ:\n\n%(verification_url)s\n\nإن كنت ترى أن هذه الرسالة وصلتك خطأً ÙØªØ¬Ø§Ù‡Ù„ها واستمتع بØÙŠØ§ØªÙƒ!" #: mediagoblin/templates/mediagoblin/auth/login.html:30 msgid "Logging in failed!" @@ -354,13 +351,7 @@ msgid "" "your web browser:\n" "\n" "%(verification_url)s" -msgstr "" -"أهلًا يا %(username)sØŒ\n" -"\n" -"Ø§ÙØªØ الرابط التالي\n" -"ÙÙŠ Ù…ØªØµÙØÙƒ Ù„ØªÙØ¹ÙŠÙ„ ØØ³Ø§Ø¨Ùƒ ÙÙŠ غنو ميدياغوبلن:\n" -"\n" -"%(verification_url)s" +msgstr "أهلًا يا %(username)sØŒ\n\nØ§ÙØªØ الرابط التالي\nÙÙŠ Ù…ØªØµÙØÙƒ Ù„ØªÙØ¹ÙŠÙ„ ØØ³Ø§Ø¨Ùƒ ÙÙŠ غنو ميدياغوبلن:\n\n%(verification_url)s" #: mediagoblin/templates/mediagoblin/edit/edit.html:29 #, python-format @@ -395,18 +386,18 @@ msgid "Media tagged with: %(tag_name)s" msgstr "" #: mediagoblin/templates/mediagoblin/media_displays/ascii.html:34 -#: mediagoblin/templates/mediagoblin/media_displays/video.html:46 +#: mediagoblin/templates/mediagoblin/media_displays/video.html:53 msgid "Original" msgstr "" -#: mediagoblin/templates/mediagoblin/media_displays/video.html:33 +#: mediagoblin/templates/mediagoblin/media_displays/video.html:40 msgid "" "Sorry, this video will not work because \n" "\t your web browser does not support HTML5 \n" "\t video." msgstr "" -#: mediagoblin/templates/mediagoblin/media_displays/video.html:36 +#: mediagoblin/templates/mediagoblin/media_displays/video.html:43 msgid "" "You can get a modern web browser that \n" "\t can play this video at <a href=\"http://getfirefox.com\">\n" @@ -431,55 +422,43 @@ msgstr "" msgid "<a href=\"%(user_url)s\">%(username)s</a>'s media" msgstr "وسائط <a href=\"%(user_url)s\">%(username)s</a>" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:72 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:46 #, python-format -msgid "Added on %(date)s." +msgid "â– Browsing media by <a href=\"%(user_url)s\">%(username)s</a>" msgstr "" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:81 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:85 msgid "Edit" msgstr "" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:85 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:89 msgid "Delete" msgstr "" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:91 -#, python-format -msgid "%(comment_count)s comment" +#: mediagoblin/templates/mediagoblin/user_pages/media.html:124 +msgid "Add a comment" msgstr "" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:93 -#, python-format -msgid "%(comment_count)s comments" -msgstr "" - -#: mediagoblin/templates/mediagoblin/user_pages/media.html:95 -msgid "No comments yet." -msgstr "" - -#: mediagoblin/templates/mediagoblin/user_pages/media.html:103 -msgid "Add one" -msgstr "" - -#: mediagoblin/templates/mediagoblin/user_pages/media.html:112 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:133 msgid "" "You can use <a " "href=\"http://daringfireball.net/projects/markdown/basics\">Markdown</a> for" " formatting." msgstr "" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:116 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:137 msgid "Add this comment" msgstr "" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:138 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:159 msgid "at" msgstr "" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:153 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:174 #, python-format -msgid "<p>â– Browsing media by <a href=\"%(user_url)s\">%(username)s</a></p>" +msgid "" +"<h3>Added on</h3>\n" +" <p>%(date)s</p>" msgstr "" #: mediagoblin/templates/mediagoblin/user_pages/media_confirm_delete.html:30 @@ -488,8 +467,8 @@ msgid "Really delete %(title)s?" msgstr "أتود ØÙ‚ًا ØØ°Ù %(title)s?" #: mediagoblin/templates/mediagoblin/user_pages/media_confirm_delete.html:50 -msgid "Delete Permanently" -msgstr "Ø§ØØ°Ù نهائيًا" +msgid "Delete permanently" +msgstr "" #: mediagoblin/templates/mediagoblin/user_pages/processing_panel.html:22 msgid "Media processing panel" @@ -555,9 +534,7 @@ msgstr "سجّل Ø£ØØ¯Ù‡Ù… ØØ³Ø§Ø¨Ù‹Ø§ بهذا الاسم، ولكننا با msgid "" "If you are that person but you've lost your verification email, you can <a " "href=\"%(login_url)s\">log in</a> and resend it." -msgstr "" -"إن كنت أنت ذلك الشخص لكنك Ùقدت رسالة التØÙ‚Ù‚ØŒ يمكنك <a " -"href=\"%(login_url)s\">الولوج</a> وإعادة إرسالها." +msgstr "إن كنت أنت ذلك الشخص لكنك Ùقدت رسالة التØÙ‚Ù‚ØŒ يمكنك <a href=\"%(login_url)s\">الولوج</a> وإعادة إرسالها." #: mediagoblin/templates/mediagoblin/user_pages/user.html:96 msgid "Here's a spot to tell others about yourself." @@ -600,8 +577,13 @@ msgstr "" msgid "Atom feed" msgstr "" -#: mediagoblin/templates/mediagoblin/utils/license.html:21 -msgid "License:" +#: mediagoblin/templates/mediagoblin/utils/geolocation_map.html:25 +msgid "Location" +msgstr "" + +#: mediagoblin/templates/mediagoblin/utils/geolocation_map.html:38 +#, python-format +msgid "View on <a href=\"%(osm_url)s\">OpenStreetMap</a>" msgstr "" #: mediagoblin/templates/mediagoblin/utils/license.html:25 @@ -620,22 +602,18 @@ msgstr "" msgid "Go to page:" msgstr "" -#: mediagoblin/templates/mediagoblin/utils/prev_next.html:27 -#: mediagoblin/templates/mediagoblin/utils/prev_next.html:32 +#: mediagoblin/templates/mediagoblin/utils/prev_next.html:28 +#: mediagoblin/templates/mediagoblin/utils/prev_next.html:33 msgid "newer" msgstr "" -#: mediagoblin/templates/mediagoblin/utils/prev_next.html:38 -#: mediagoblin/templates/mediagoblin/utils/prev_next.html:43 +#: mediagoblin/templates/mediagoblin/utils/prev_next.html:39 +#: mediagoblin/templates/mediagoblin/utils/prev_next.html:44 msgid "older" msgstr "" #: mediagoblin/templates/mediagoblin/utils/tags.html:20 -msgid "View more media tagged with" -msgstr "" - -#: mediagoblin/templates/mediagoblin/utils/tags.html:25 -msgid "or" +msgid "Tagged with" msgstr "" #: mediagoblin/tools/exif.py:68 @@ -646,24 +624,22 @@ msgstr "" msgid "I am sure I want to delete this" msgstr "أنا متأكد من رغبتي Ø¨ØØ°Ù هذا العمل" -#: mediagoblin/user_pages/views.py:155 +#: mediagoblin/user_pages/views.py:153 msgid "Oops, your comment was empty." msgstr "" -#: mediagoblin/user_pages/views.py:161 +#: mediagoblin/user_pages/views.py:159 msgid "Your comment has been posted!" msgstr "" -#: mediagoblin/user_pages/views.py:183 +#: mediagoblin/user_pages/views.py:185 msgid "You deleted the media." msgstr "" -#: mediagoblin/user_pages/views.py:190 +#: mediagoblin/user_pages/views.py:192 msgid "The media was not deleted because you didn't check that you were sure." msgstr "" -#: mediagoblin/user_pages/views.py:198 +#: mediagoblin/user_pages/views.py:200 msgid "You are about to delete another user's media. Proceed with caution." msgstr "أنت على وشك ØØ°Ù وسائط مستخدم آخر. كن ØØ°Ø±Ù‹Ø§ أثناء العملية." - - diff --git a/mediagoblin/i18n/ca/LC_MESSAGES/mediagoblin.mo b/mediagoblin/i18n/ca/LC_MESSAGES/mediagoblin.mo Binary files differindex 6fdb7cbf..c4b4ee2f 100644 --- a/mediagoblin/i18n/ca/LC_MESSAGES/mediagoblin.mo +++ b/mediagoblin/i18n/ca/LC_MESSAGES/mediagoblin.mo diff --git a/mediagoblin/i18n/ca/LC_MESSAGES/mediagoblin.po b/mediagoblin/i18n/ca/LC_MESSAGES/mediagoblin.po index 0c956b9b..08269aed 100644 --- a/mediagoblin/i18n/ca/LC_MESSAGES/mediagoblin.po +++ b/mediagoblin/i18n/ca/LC_MESSAGES/mediagoblin.po @@ -8,9 +8,9 @@ msgid "" msgstr "" "Project-Id-Version: GNU MediaGoblin\n" -"Report-Msgid-Bugs-To: http://bugs.foocorp.net/projects/mediagoblin/issues\n" -"POT-Creation-Date: 2012-01-29 13:31-0600\n" -"PO-Revision-Date: 2012-01-29 19:29+0000\n" +"Report-Msgid-Bugs-To: http://issues.mediagoblin.org/\n" +"POT-Creation-Date: 2012-03-18 15:16-0500\n" +"PO-Revision-Date: 2012-03-18 20:14+0000\n" "Last-Translator: cwebber <cwebber@dustycloud.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n" "MIME-Version: 1.0\n" @@ -20,7 +20,7 @@ msgstr "" "Language: ca\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" -#: mediagoblin/processing.py:143 +#: mediagoblin/processing.py:154 msgid "Invalid file given for media type." msgstr "Aquest tipus de fitxer no és và lid." @@ -40,55 +40,52 @@ msgstr "Adreça electrònica" msgid "Sorry, registration is disabled on this instance." msgstr "Ho sentim, el registre està desactivat en aquest cas." -#: mediagoblin/auth/views.py:73 +#: mediagoblin/auth/views.py:75 msgid "Sorry, a user with that name already exists." msgstr "Lamentablement aquest usuari ja existeix." -#: mediagoblin/auth/views.py:77 +#: mediagoblin/auth/views.py:79 msgid "Sorry, a user with that email address already exists." msgstr "" -#: mediagoblin/auth/views.py:180 +#: mediagoblin/auth/views.py:182 msgid "" "Your email address has been verified. You may now login, edit your profile, " "and submit images!" -msgstr "" -"Ja s'ha verificat la vostra adreça electrònica. Ara podeu entrar, editar el " -"vostre perfil i penjar imatge!" +msgstr "Ja s'ha verificat la vostra adreça electrònica. Ara podeu entrar, editar el vostre perfil i penjar imatge!" -#: mediagoblin/auth/views.py:186 +#: mediagoblin/auth/views.py:188 msgid "The verification key or user id is incorrect" -msgstr "" -"La clau de verificació o la identificació de l'usuari no són correctes." +msgstr "La clau de verificació o la identificació de l'usuari no són correctes." -#: mediagoblin/auth/views.py:204 +#: mediagoblin/auth/views.py:206 msgid "You must be logged in so we know who to send the email to!" msgstr "" -#: mediagoblin/auth/views.py:212 +#: mediagoblin/auth/views.py:214 msgid "You've already verified your email address!" msgstr "" -#: mediagoblin/auth/views.py:225 +#: mediagoblin/auth/views.py:227 msgid "Resent your verification email." msgstr "Torna'm a enviar el correu de verificació" -#: mediagoblin/auth/views.py:260 +#: mediagoblin/auth/views.py:262 msgid "" "An email has been sent with instructions on how to change your password." msgstr "" -#: mediagoblin/auth/views.py:270 +#: mediagoblin/auth/views.py:272 msgid "" "Could not send password recovery email as your username is inactive or your " "account's email address has not been verified." msgstr "" -#: mediagoblin/auth/views.py:282 +#: mediagoblin/auth/views.py:284 msgid "Couldn't find someone with that username or email." msgstr "" -#: mediagoblin/auth/views.py:330 +#: mediagoblin/auth/views.py:332 msgid "You can now log in using your new password." msgstr "" @@ -131,6 +128,7 @@ msgid "" msgstr "" #: mediagoblin/edit/forms.py:44 mediagoblin/submit/forms.py:41 +#: mediagoblin/templates/mediagoblin/utils/license.html:20 msgid "License" msgstr "" @@ -154,27 +152,27 @@ msgstr "" msgid "New password" msgstr "" -#: mediagoblin/edit/views.py:68 +#: mediagoblin/edit/views.py:67 msgid "An entry with that slug already exists for this user." msgstr "" -#: mediagoblin/edit/views.py:92 +#: mediagoblin/edit/views.py:88 msgid "You are editing another user's media. Proceed with caution." msgstr "Esteu editant fitxers d'un altre usuari. Aneu amb compte." -#: mediagoblin/edit/views.py:162 +#: mediagoblin/edit/views.py:158 msgid "You are editing a user's profile. Proceed with caution." msgstr "Esteu editant el perfil d'un usuari. Aneu amb compte" -#: mediagoblin/edit/views.py:180 +#: mediagoblin/edit/views.py:174 msgid "Profile changes saved" msgstr "" -#: mediagoblin/edit/views.py:206 +#: mediagoblin/edit/views.py:200 msgid "Wrong password" msgstr "" -#: mediagoblin/edit/views.py:222 +#: mediagoblin/edit/views.py:216 msgid "Account settings saved" msgstr "" @@ -194,7 +192,7 @@ msgstr "Fitxer" msgid "You must provide a file." msgstr "Heu d'escollir un fitxer." -#: mediagoblin/submit/views.py:158 +#: mediagoblin/submit/views.py:156 msgid "Woohoo! Submitted!" msgstr "Visca! S'ha enviat!" @@ -214,9 +212,7 @@ msgstr "Sembla que no hi ha cap pà gina en aquesta adreça. Ho sentim." msgid "" "If you're sure the address is correct, maybe the page you're looking for has" " been moved or deleted." -msgstr "" -"Si esteu convençut que l'adreça és correcta, pot ser que la pà gina que " -"cerqueu s'hagi canviat d'ubicació o s'hagi eliminat." +msgstr "Si esteu convençut que l'adreça és correcta, pot ser que la pà gina que cerqueu s'hagi canviat d'ubicació o s'hagi eliminat." #: mediagoblin/templates/mediagoblin/base.html:46 msgid "MediaGoblin logo" @@ -244,7 +240,15 @@ msgstr "Entra" #: mediagoblin/templates/mediagoblin/base.html:84 msgid "" "Powered by <a href=\"http://mediagoblin.org\">MediaGoblin</a>, a <a " -"href=\"http://gnu.org/\">GNU</a> project" +"href=\"http://gnu.org/\">GNU</a> project." +msgstr "" + +#: mediagoblin/templates/mediagoblin/base.html:87 +#, python-format +msgid "" +"Released under the <a " +"href=\"http://www.fsf.org/licensing/licenses/agpl-3.0.html\">AGPL</a>. <a " +"href=\"%(source_link)s\">Source code</a> available." msgstr "" #: mediagoblin/templates/mediagoblin/root.html:24 @@ -346,13 +350,7 @@ msgid "" "your web browser:\n" "\n" "%(verification_url)s" -msgstr "" -"Hi %(username)s,\n" -"\n" -"to activate your GNU MediaGoblin account, open the following URL in\n" -"your web browser:\n" -"\n" -"%(verification_url)s" +msgstr "Hi %(username)s,\n\nto activate your GNU MediaGoblin account, open the following URL in\nyour web browser:\n\n%(verification_url)s" #: mediagoblin/templates/mediagoblin/edit/edit.html:29 #, python-format @@ -387,18 +385,18 @@ msgid "Media tagged with: %(tag_name)s" msgstr "" #: mediagoblin/templates/mediagoblin/media_displays/ascii.html:34 -#: mediagoblin/templates/mediagoblin/media_displays/video.html:46 +#: mediagoblin/templates/mediagoblin/media_displays/video.html:53 msgid "Original" msgstr "" -#: mediagoblin/templates/mediagoblin/media_displays/video.html:33 +#: mediagoblin/templates/mediagoblin/media_displays/video.html:40 msgid "" "Sorry, this video will not work because \n" "\t your web browser does not support HTML5 \n" "\t video." msgstr "" -#: mediagoblin/templates/mediagoblin/media_displays/video.html:36 +#: mediagoblin/templates/mediagoblin/media_displays/video.html:43 msgid "" "You can get a modern web browser that \n" "\t can play this video at <a href=\"http://getfirefox.com\">\n" @@ -423,55 +421,43 @@ msgstr "" msgid "<a href=\"%(user_url)s\">%(username)s</a>'s media" msgstr "<a href=\"%(user_url)s\">%(username)s</a>'s media" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:72 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:46 #, python-format -msgid "Added on %(date)s." +msgid "â– Browsing media by <a href=\"%(user_url)s\">%(username)s</a>" msgstr "" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:81 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:85 msgid "Edit" msgstr "" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:85 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:89 msgid "Delete" msgstr "" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:91 -#, python-format -msgid "%(comment_count)s comment" -msgstr "" - -#: mediagoblin/templates/mediagoblin/user_pages/media.html:93 -#, python-format -msgid "%(comment_count)s comments" -msgstr "" - -#: mediagoblin/templates/mediagoblin/user_pages/media.html:95 -msgid "No comments yet." +#: mediagoblin/templates/mediagoblin/user_pages/media.html:124 +msgid "Add a comment" msgstr "" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:103 -msgid "Add one" -msgstr "" - -#: mediagoblin/templates/mediagoblin/user_pages/media.html:112 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:133 msgid "" "You can use <a " "href=\"http://daringfireball.net/projects/markdown/basics\">Markdown</a> for" " formatting." msgstr "" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:116 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:137 msgid "Add this comment" msgstr "" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:138 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:159 msgid "at" msgstr "" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:153 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:174 #, python-format -msgid "<p>â– Browsing media by <a href=\"%(user_url)s\">%(username)s</a></p>" +msgid "" +"<h3>Added on</h3>\n" +" <p>%(date)s</p>" msgstr "" #: mediagoblin/templates/mediagoblin/user_pages/media_confirm_delete.html:30 @@ -480,7 +466,7 @@ msgid "Really delete %(title)s?" msgstr "" #: mediagoblin/templates/mediagoblin/user_pages/media_confirm_delete.html:50 -msgid "Delete Permanently" +msgid "Delete permanently" msgstr "" #: mediagoblin/templates/mediagoblin/user_pages/processing_panel.html:22 @@ -540,18 +526,14 @@ msgstr "Torna'm a enviar el correu de verificació" msgid "" "Someone has registered an account with this username, but it still has to be" " activated." -msgstr "" -"Algú ja ha registrat un compte amb aquest nom d'usuari, però encara l'ha " -"d'activar." +msgstr "Algú ja ha registrat un compte amb aquest nom d'usuari, però encara l'ha d'activar." #: mediagoblin/templates/mediagoblin/user_pages/user.html:79 #, python-format msgid "" "If you are that person but you've lost your verification email, you can <a " "href=\"%(login_url)s\">log in</a> and resend it." -msgstr "" -"Si siu aqeust usuari però heu perdut el correu de verificació, podeu <a " -"href=\"%(login_url)s\">entrar</a> i tornar-lo a enviar." +msgstr "Si siu aqeust usuari però heu perdut el correu de verificació, podeu <a href=\"%(login_url)s\">entrar</a> i tornar-lo a enviar." #: mediagoblin/templates/mediagoblin/user_pages/user.html:96 msgid "Here's a spot to tell others about yourself." @@ -594,8 +576,13 @@ msgstr "Icona RSS" msgid "Atom feed" msgstr "" -#: mediagoblin/templates/mediagoblin/utils/license.html:21 -msgid "License:" +#: mediagoblin/templates/mediagoblin/utils/geolocation_map.html:25 +msgid "Location" +msgstr "" + +#: mediagoblin/templates/mediagoblin/utils/geolocation_map.html:38 +#, python-format +msgid "View on <a href=\"%(osm_url)s\">OpenStreetMap</a>" msgstr "" #: mediagoblin/templates/mediagoblin/utils/license.html:25 @@ -614,22 +601,18 @@ msgstr "" msgid "Go to page:" msgstr "" -#: mediagoblin/templates/mediagoblin/utils/prev_next.html:27 -#: mediagoblin/templates/mediagoblin/utils/prev_next.html:32 +#: mediagoblin/templates/mediagoblin/utils/prev_next.html:28 +#: mediagoblin/templates/mediagoblin/utils/prev_next.html:33 msgid "newer" msgstr "" -#: mediagoblin/templates/mediagoblin/utils/prev_next.html:38 -#: mediagoblin/templates/mediagoblin/utils/prev_next.html:43 +#: mediagoblin/templates/mediagoblin/utils/prev_next.html:39 +#: mediagoblin/templates/mediagoblin/utils/prev_next.html:44 msgid "older" msgstr "" #: mediagoblin/templates/mediagoblin/utils/tags.html:20 -msgid "View more media tagged with" -msgstr "" - -#: mediagoblin/templates/mediagoblin/utils/tags.html:25 -msgid "or" +msgid "Tagged with" msgstr "" #: mediagoblin/tools/exif.py:68 @@ -640,24 +623,22 @@ msgstr "" msgid "I am sure I want to delete this" msgstr "" -#: mediagoblin/user_pages/views.py:155 +#: mediagoblin/user_pages/views.py:153 msgid "Oops, your comment was empty." msgstr "" -#: mediagoblin/user_pages/views.py:161 +#: mediagoblin/user_pages/views.py:159 msgid "Your comment has been posted!" msgstr "" -#: mediagoblin/user_pages/views.py:183 +#: mediagoblin/user_pages/views.py:185 msgid "You deleted the media." msgstr "" -#: mediagoblin/user_pages/views.py:190 +#: mediagoblin/user_pages/views.py:192 msgid "The media was not deleted because you didn't check that you were sure." msgstr "" -#: mediagoblin/user_pages/views.py:198 +#: mediagoblin/user_pages/views.py:200 msgid "You are about to delete another user's media. Proceed with caution." msgstr "" - - diff --git a/mediagoblin/i18n/da/LC_MESSAGES/mediagoblin.mo b/mediagoblin/i18n/da/LC_MESSAGES/mediagoblin.mo Binary files differnew file mode 100644 index 00000000..8881ae35 --- /dev/null +++ b/mediagoblin/i18n/da/LC_MESSAGES/mediagoblin.mo diff --git a/mediagoblin/i18n/da/LC_MESSAGES/mediagoblin.po b/mediagoblin/i18n/da/LC_MESSAGES/mediagoblin.po new file mode 100644 index 00000000..2bc53d10 --- /dev/null +++ b/mediagoblin/i18n/da/LC_MESSAGES/mediagoblin.po @@ -0,0 +1,645 @@ +# Translations template for PROJECT. +# Copyright (C) 2012 ORGANIZATION +# This file is distributed under the same license as the PROJECT project. +# +# Translators: +# Morten Juhl-Johansen Zölde-Fejér <morten@writtenandread.net>, 2012. +msgid "" +msgstr "" +"Project-Id-Version: GNU MediaGoblin\n" +"Report-Msgid-Bugs-To: http://issues.mediagoblin.org/\n" +"POT-Creation-Date: 2012-02-26 15:51-0600\n" +"PO-Revision-Date: 2012-03-14 22:49+0000\n" +"Last-Translator: Morten Juhl-Johansen Zölde-Fejér <morten@writtenandread.net>\n" +"Language-Team: LANGUAGE <LL@li.org>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Generated-By: Babel 0.9.6\n" +"Language: da\n" +"Plural-Forms: nplurals=2; plural=(n != 1)\n" + +#: mediagoblin/processing.py:153 +msgid "Invalid file given for media type." +msgstr "" + +#: mediagoblin/auth/forms.py:25 mediagoblin/auth/forms.py:41 +msgid "Username" +msgstr "" + +#: mediagoblin/auth/forms.py:30 mediagoblin/auth/forms.py:45 +msgid "Password" +msgstr "" + +#: mediagoblin/auth/forms.py:34 +msgid "Email address" +msgstr "" + +#: mediagoblin/auth/views.py:55 +msgid "Sorry, registration is disabled on this instance." +msgstr "" + +#: mediagoblin/auth/views.py:75 +msgid "Sorry, a user with that name already exists." +msgstr "" + +#: mediagoblin/auth/views.py:79 +msgid "Sorry, a user with that email address already exists." +msgstr "" + +#: mediagoblin/auth/views.py:182 +msgid "" +"Your email address has been verified. You may now login, edit your profile, " +"and submit images!" +msgstr "" + +#: mediagoblin/auth/views.py:188 +msgid "The verification key or user id is incorrect" +msgstr "" + +#: mediagoblin/auth/views.py:206 +msgid "You must be logged in so we know who to send the email to!" +msgstr "" + +#: mediagoblin/auth/views.py:214 +msgid "You've already verified your email address!" +msgstr "" + +#: mediagoblin/auth/views.py:227 +msgid "Resent your verification email." +msgstr "Email til godkendelse sendt igen." + +#: mediagoblin/auth/views.py:262 +msgid "" +"An email has been sent with instructions on how to change your password." +msgstr "" + +#: mediagoblin/auth/views.py:272 +msgid "" +"Could not send password recovery email as your username is inactive or your " +"account's email address has not been verified." +msgstr "" + +#: mediagoblin/auth/views.py:284 +msgid "Couldn't find someone with that username or email." +msgstr "" + +#: mediagoblin/auth/views.py:332 +msgid "You can now log in using your new password." +msgstr "" + +#: mediagoblin/edit/forms.py:25 mediagoblin/submit/forms.py:28 +msgid "Title" +msgstr "" + +#: mediagoblin/edit/forms.py:28 mediagoblin/submit/forms.py:31 +msgid "Description of this work" +msgstr "" + +#: mediagoblin/edit/forms.py:29 mediagoblin/edit/forms.py:52 +#: mediagoblin/submit/forms.py:32 +msgid "" +"You can use\n" +" <a href=\"http://daringfireball.net/projects/markdown/basics\">\n" +" Markdown</a> for formatting." +msgstr "" + +#: mediagoblin/edit/forms.py:33 mediagoblin/submit/forms.py:36 +msgid "Tags" +msgstr "" + +#: mediagoblin/edit/forms.py:35 mediagoblin/submit/forms.py:38 +msgid "Separate tags by commas." +msgstr "" + +#: mediagoblin/edit/forms.py:38 +msgid "Slug" +msgstr "" + +#: mediagoblin/edit/forms.py:39 +msgid "The slug can't be empty" +msgstr "" + +#: mediagoblin/edit/forms.py:40 +msgid "" +"The title part of this media's address. You usually don't need to change " +"this." +msgstr "" + +#: mediagoblin/edit/forms.py:44 mediagoblin/submit/forms.py:41 +msgid "License" +msgstr "" + +#: mediagoblin/edit/forms.py:50 +msgid "Bio" +msgstr "" + +#: mediagoblin/edit/forms.py:56 +msgid "Website" +msgstr "" + +#: mediagoblin/edit/forms.py:63 +msgid "Old password" +msgstr "" + +#: mediagoblin/edit/forms.py:65 +msgid "Enter your old password to prove you own this account." +msgstr "" + +#: mediagoblin/edit/forms.py:68 +msgid "New password" +msgstr "" + +#: mediagoblin/edit/views.py:68 +msgid "An entry with that slug already exists for this user." +msgstr "" + +#: mediagoblin/edit/views.py:89 +msgid "You are editing another user's media. Proceed with caution." +msgstr "" + +#: mediagoblin/edit/views.py:159 +msgid "You are editing a user's profile. Proceed with caution." +msgstr "" + +#: mediagoblin/edit/views.py:175 +msgid "Profile changes saved" +msgstr "" + +#: mediagoblin/edit/views.py:201 +msgid "Wrong password" +msgstr "" + +#: mediagoblin/edit/views.py:217 +msgid "Account settings saved" +msgstr "" + +#: mediagoblin/media_types/__init__.py:77 +msgid "Could not extract any file extension from \"{filename}\"" +msgstr "" + +#: mediagoblin/media_types/__init__.py:88 +msgid "Sorry, I don't support that file type :(" +msgstr "" + +#: mediagoblin/submit/forms.py:26 +msgid "File" +msgstr "" + +#: mediagoblin/submit/views.py:54 +msgid "You must provide a file." +msgstr "" + +#: mediagoblin/submit/views.py:156 +msgid "Woohoo! Submitted!" +msgstr "" + +#: mediagoblin/templates/mediagoblin/404.html:22 +msgid "Image of 404 goblin stressing out" +msgstr "" + +#: mediagoblin/templates/mediagoblin/404.html:23 +msgid "Oops!" +msgstr "" + +#: mediagoblin/templates/mediagoblin/404.html:24 +msgid "There doesn't seem to be a page at this address. Sorry!" +msgstr "" + +#: mediagoblin/templates/mediagoblin/404.html:26 +msgid "" +"If you're sure the address is correct, maybe the page you're looking for has" +" been moved or deleted." +msgstr "" + +#: mediagoblin/templates/mediagoblin/base.html:46 +msgid "MediaGoblin logo" +msgstr "" + +#: mediagoblin/templates/mediagoblin/base.html:51 +#: mediagoblin/templates/mediagoblin/user_pages/user.html:157 +msgid "Add media" +msgstr "" + +#: mediagoblin/templates/mediagoblin/base.html:62 +msgid "Verify your email!" +msgstr "" + +#: mediagoblin/templates/mediagoblin/base.html:69 +msgid "log out" +msgstr "" + +#: mediagoblin/templates/mediagoblin/base.html:72 +#: mediagoblin/templates/mediagoblin/auth/login.html:27 +#: mediagoblin/templates/mediagoblin/auth/login.html:45 +msgid "Log in" +msgstr "Log ind" + +#: mediagoblin/templates/mediagoblin/base.html:84 +msgid "" +"Powered by <a href=\"http://mediagoblin.org\">MediaGoblin</a>, a <a " +"href=\"http://gnu.org/\">GNU</a> project" +msgstr "" + +#: mediagoblin/templates/mediagoblin/root.html:24 +msgid "Explore" +msgstr "" + +#: mediagoblin/templates/mediagoblin/root.html:26 +msgid "Hi there, welcome to this MediaGoblin site!" +msgstr "" + +#: mediagoblin/templates/mediagoblin/root.html:28 +msgid "" +"This site is running <a href=\"http://mediagoblin.org\">MediaGoblin</a>, an " +"extraordinarily great piece of media hosting software." +msgstr "" + +#: mediagoblin/templates/mediagoblin/root.html:29 +msgid "" +"To add your own media, place comments, save your favourites and more, you " +"can log in with your MediaGoblin account." +msgstr "" + +#: mediagoblin/templates/mediagoblin/root.html:31 +msgid "Don't have one yet? It's easy!" +msgstr "" + +#: mediagoblin/templates/mediagoblin/root.html:32 +#, python-format +msgid "" +"<a class=\"button_action_highlight\" href=\"%(register_url)s\">Create an account at this site</a>\n" +" or\n" +" <a class=\"button_action\" href=\"http://wiki.mediagoblin.org/HackingHowto\">Set up MediaGoblin on your own server</a>" +msgstr "" + +#: mediagoblin/templates/mediagoblin/root.html:40 +msgid "Most recent media" +msgstr "" + +#: mediagoblin/templates/mediagoblin/auth/change_fp.html:32 +msgid "Set your new password" +msgstr "" + +#: mediagoblin/templates/mediagoblin/auth/change_fp.html:35 +msgid "Set password" +msgstr "" + +#: mediagoblin/templates/mediagoblin/auth/forgot_password.html:27 +msgid "Recover password" +msgstr "" + +#: mediagoblin/templates/mediagoblin/auth/forgot_password.html:30 +msgid "Send instructions" +msgstr "" + +#: mediagoblin/templates/mediagoblin/auth/fp_verification_email.txt:19 +#, python-format +msgid "" +"Hi %(username)s,\n" +"\n" +"to change your GNU MediaGoblin password, open the following URL in \n" +"your web browser:\n" +"\n" +"%(verification_url)s\n" +"\n" +"If you think this is an error, just ignore this email and continue being\n" +"a happy goblin!" +msgstr "" + +#: mediagoblin/templates/mediagoblin/auth/login.html:30 +msgid "Logging in failed!" +msgstr "" + +#: mediagoblin/templates/mediagoblin/auth/login.html:35 +msgid "Don't have an account yet?" +msgstr "Har du endnu ikke en konto?" + +#: mediagoblin/templates/mediagoblin/auth/login.html:36 +msgid "Create one here!" +msgstr "Opret en her!" + +#: mediagoblin/templates/mediagoblin/auth/login.html:42 +msgid "Forgot your password?" +msgstr "" + +#: mediagoblin/templates/mediagoblin/auth/register.html:32 +msgid "Create an account!" +msgstr "Opret en konto!" + +#: mediagoblin/templates/mediagoblin/auth/register.html:36 +msgid "Create" +msgstr "" + +#: mediagoblin/templates/mediagoblin/auth/verification_email.txt:19 +#, python-format +msgid "" +"Hi %(username)s,\n" +"\n" +"to activate your GNU MediaGoblin account, open the following URL in\n" +"your web browser:\n" +"\n" +"%(verification_url)s" +msgstr "" + +#: mediagoblin/templates/mediagoblin/edit/edit.html:29 +#, python-format +msgid "Editing %(media_title)s" +msgstr "" + +#: mediagoblin/templates/mediagoblin/edit/edit.html:36 +#: mediagoblin/templates/mediagoblin/user_pages/media_confirm_delete.html:49 +msgid "Cancel" +msgstr "" + +#: mediagoblin/templates/mediagoblin/edit/edit.html:37 +#: mediagoblin/templates/mediagoblin/edit/edit_account.html:40 +#: mediagoblin/templates/mediagoblin/edit/edit_profile.html:35 +msgid "Save changes" +msgstr "" + +#: mediagoblin/templates/mediagoblin/edit/edit_account.html:34 +#, python-format +msgid "Changing %(username)s's account settings" +msgstr "" + +#: mediagoblin/templates/mediagoblin/edit/edit_profile.html:29 +#, python-format +msgid "Editing %(username)s's profile" +msgstr "" + +#: mediagoblin/templates/mediagoblin/listings/tag.html:30 +#: mediagoblin/templates/mediagoblin/listings/tag.html:35 +#, python-format +msgid "Media tagged with: %(tag_name)s" +msgstr "" + +#: mediagoblin/templates/mediagoblin/media_displays/ascii.html:34 +#: mediagoblin/templates/mediagoblin/media_displays/video.html:46 +msgid "Original" +msgstr "" + +#: mediagoblin/templates/mediagoblin/media_displays/video.html:33 +msgid "" +"Sorry, this video will not work because \n" +"\t your web browser does not support HTML5 \n" +"\t video." +msgstr "" + +#: mediagoblin/templates/mediagoblin/media_displays/video.html:36 +msgid "" +"You can get a modern web browser that \n" +"\t can play this video at <a href=\"http://getfirefox.com\">\n" +"\t http://getfirefox.com</a>!" +msgstr "" + +#: mediagoblin/templates/mediagoblin/submit/start.html:26 +msgid "Add your media" +msgstr "" + +#: mediagoblin/templates/mediagoblin/submit/start.html:30 +msgid "Add" +msgstr "" + +#: mediagoblin/templates/mediagoblin/user_pages/gallery.html:30 +#, python-format +msgid "%(username)s's media" +msgstr "" + +#: mediagoblin/templates/mediagoblin/user_pages/gallery.html:37 +#, python-format +msgid "<a href=\"%(user_url)s\">%(username)s</a>'s media" +msgstr "" + +#: mediagoblin/templates/mediagoblin/user_pages/media.html:72 +#, python-format +msgid "Added on %(date)s." +msgstr "" + +#: mediagoblin/templates/mediagoblin/user_pages/media.html:81 +msgid "Edit" +msgstr "" + +#: mediagoblin/templates/mediagoblin/user_pages/media.html:85 +msgid "Delete" +msgstr "" + +#: mediagoblin/templates/mediagoblin/user_pages/media.html:91 +#, python-format +msgid "%(comment_count)s comment" +msgstr "" + +#: mediagoblin/templates/mediagoblin/user_pages/media.html:93 +#, python-format +msgid "%(comment_count)s comments" +msgstr "" + +#: mediagoblin/templates/mediagoblin/user_pages/media.html:95 +msgid "No comments yet." +msgstr "" + +#: mediagoblin/templates/mediagoblin/user_pages/media.html:103 +msgid "Add one" +msgstr "" + +#: mediagoblin/templates/mediagoblin/user_pages/media.html:112 +msgid "" +"You can use <a " +"href=\"http://daringfireball.net/projects/markdown/basics\">Markdown</a> for" +" formatting." +msgstr "" + +#: mediagoblin/templates/mediagoblin/user_pages/media.html:116 +msgid "Add this comment" +msgstr "" + +#: mediagoblin/templates/mediagoblin/user_pages/media.html:138 +msgid "at" +msgstr "" + +#: mediagoblin/templates/mediagoblin/user_pages/media.html:153 +#, python-format +msgid "<p>â– Browsing media by <a href=\"%(user_url)s\">%(username)s</a></p>" +msgstr "" + +#: mediagoblin/templates/mediagoblin/user_pages/media_confirm_delete.html:30 +#, python-format +msgid "Really delete %(title)s?" +msgstr "" + +#: mediagoblin/templates/mediagoblin/user_pages/media_confirm_delete.html:50 +msgid "Delete Permanently" +msgstr "" + +#: mediagoblin/templates/mediagoblin/user_pages/processing_panel.html:22 +msgid "Media processing panel" +msgstr "" + +#: mediagoblin/templates/mediagoblin/user_pages/processing_panel.html:25 +msgid "" +"You can track the state of media being processed for your gallery here." +msgstr "" + +#: mediagoblin/templates/mediagoblin/user_pages/processing_panel.html:28 +msgid "Media in-processing" +msgstr "" + +#: mediagoblin/templates/mediagoblin/user_pages/processing_panel.html:46 +msgid "No media in-processing" +msgstr "" + +#: mediagoblin/templates/mediagoblin/user_pages/processing_panel.html:50 +msgid "These uploads failed to process:" +msgstr "" + +#: mediagoblin/templates/mediagoblin/user_pages/user.html:31 +#: mediagoblin/templates/mediagoblin/user_pages/user.html:89 +#, python-format +msgid "%(username)s's profile" +msgstr "" + +#: mediagoblin/templates/mediagoblin/user_pages/user.html:43 +msgid "Sorry, no such user found." +msgstr "" + +#: mediagoblin/templates/mediagoblin/user_pages/user.html:50 +#: mediagoblin/templates/mediagoblin/user_pages/user.html:70 +msgid "Email verification needed" +msgstr "" + +#: mediagoblin/templates/mediagoblin/user_pages/user.html:53 +msgid "Almost done! Your account still needs to be activated." +msgstr "" + +#: mediagoblin/templates/mediagoblin/user_pages/user.html:58 +msgid "" +"An email should arrive in a few moments with instructions on how to do so." +msgstr "" + +#: mediagoblin/templates/mediagoblin/user_pages/user.html:62 +msgid "In case it doesn't:" +msgstr "" + +#: mediagoblin/templates/mediagoblin/user_pages/user.html:65 +msgid "Resend verification email" +msgstr "" + +#: mediagoblin/templates/mediagoblin/user_pages/user.html:73 +msgid "" +"Someone has registered an account with this username, but it still has to be" +" activated." +msgstr "" + +#: mediagoblin/templates/mediagoblin/user_pages/user.html:79 +#, python-format +msgid "" +"If you are that person but you've lost your verification email, you can <a " +"href=\"%(login_url)s\">log in</a> and resend it." +msgstr "" + +#: mediagoblin/templates/mediagoblin/user_pages/user.html:96 +msgid "Here's a spot to tell others about yourself." +msgstr "" + +#: mediagoblin/templates/mediagoblin/user_pages/user.html:101 +#: mediagoblin/templates/mediagoblin/user_pages/user.html:118 +msgid "Edit profile" +msgstr "" + +#: mediagoblin/templates/mediagoblin/user_pages/user.html:106 +msgid "This user hasn't filled in their profile (yet)." +msgstr "" + +#: mediagoblin/templates/mediagoblin/user_pages/user.html:125 +msgid "Change account settings" +msgstr "" + +#: mediagoblin/templates/mediagoblin/user_pages/user.html:138 +#, python-format +msgid "View all of %(username)s's media" +msgstr "" + +#: mediagoblin/templates/mediagoblin/user_pages/user.html:151 +msgid "" +"This is where your media will appear, but you don't seem to have added " +"anything yet." +msgstr "" + +#: mediagoblin/templates/mediagoblin/user_pages/user.html:163 +#: mediagoblin/templates/mediagoblin/utils/object_gallery.html:72 +msgid "There doesn't seem to be any media here yet..." +msgstr "" + +#: mediagoblin/templates/mediagoblin/utils/feed_link.html:21 +msgid "feed icon" +msgstr "" + +#: mediagoblin/templates/mediagoblin/utils/feed_link.html:23 +msgid "Atom feed" +msgstr "" + +#: mediagoblin/templates/mediagoblin/utils/license.html:21 +msgid "License:" +msgstr "" + +#: mediagoblin/templates/mediagoblin/utils/license.html:25 +msgid "All rights reserved" +msgstr "" + +#: mediagoblin/templates/mediagoblin/utils/pagination.html:39 +msgid "↠Newer" +msgstr "" + +#: mediagoblin/templates/mediagoblin/utils/pagination.html:45 +msgid "Older →" +msgstr "" + +#: mediagoblin/templates/mediagoblin/utils/pagination.html:48 +msgid "Go to page:" +msgstr "" + +#: mediagoblin/templates/mediagoblin/utils/prev_next.html:27 +#: mediagoblin/templates/mediagoblin/utils/prev_next.html:32 +msgid "newer" +msgstr "" + +#: mediagoblin/templates/mediagoblin/utils/prev_next.html:38 +#: mediagoblin/templates/mediagoblin/utils/prev_next.html:43 +msgid "older" +msgstr "" + +#: mediagoblin/templates/mediagoblin/utils/tags.html:20 +msgid "View more media tagged with" +msgstr "" + +#: mediagoblin/templates/mediagoblin/utils/tags.html:25 +msgid "or" +msgstr "" + +#: mediagoblin/tools/exif.py:68 +msgid "Could not read the image file." +msgstr "" + +#: mediagoblin/user_pages/forms.py:30 +msgid "I am sure I want to delete this" +msgstr "" + +#: mediagoblin/user_pages/views.py:153 +msgid "Oops, your comment was empty." +msgstr "" + +#: mediagoblin/user_pages/views.py:159 +msgid "Your comment has been posted!" +msgstr "" + +#: mediagoblin/user_pages/views.py:181 +msgid "You deleted the media." +msgstr "" + +#: mediagoblin/user_pages/views.py:188 +msgid "The media was not deleted because you didn't check that you were sure." +msgstr "" + +#: mediagoblin/user_pages/views.py:196 +msgid "You are about to delete another user's media. Proceed with caution." +msgstr "" diff --git a/mediagoblin/i18n/de/LC_MESSAGES/mediagoblin.mo b/mediagoblin/i18n/de/LC_MESSAGES/mediagoblin.mo Binary files differindex e3ba1606..d15e0fb8 100644 --- a/mediagoblin/i18n/de/LC_MESSAGES/mediagoblin.mo +++ b/mediagoblin/i18n/de/LC_MESSAGES/mediagoblin.mo diff --git a/mediagoblin/i18n/de/LC_MESSAGES/mediagoblin.po b/mediagoblin/i18n/de/LC_MESSAGES/mediagoblin.po index e435971a..a48c42e7 100644 --- a/mediagoblin/i18n/de/LC_MESSAGES/mediagoblin.po +++ b/mediagoblin/i18n/de/LC_MESSAGES/mediagoblin.po @@ -17,10 +17,10 @@ msgid "" msgstr "" "Project-Id-Version: GNU MediaGoblin\n" "Report-Msgid-Bugs-To: http://issues.mediagoblin.org/\n" -"POT-Creation-Date: 2012-01-29 13:47-0600\n" -"PO-Revision-Date: 2012-02-05 20:23+0000\n" -"Last-Translator: Jan-Christoph Borchardt <jan@unhosted.org>\n" -"Language-Team: German (http://www.transifex.net/projects/p/mediagoblin/team/de/)\n" +"POT-Creation-Date: 2012-03-18 15:16-0500\n" +"PO-Revision-Date: 2012-03-18 20:14+0000\n" +"Last-Translator: cwebber <cwebber@dustycloud.org>\n" +"Language-Team: German (http://www.transifex.net/projects/p/mediagoblin/language/de/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" @@ -28,7 +28,7 @@ msgstr "" "Language: de\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" -#: mediagoblin/processing.py:143 +#: mediagoblin/processing.py:154 msgid "Invalid file given for media type." msgstr "Die Datei stimmt nicht mit dem gewählten Medientyp überein." @@ -48,59 +48,52 @@ msgstr "E-Mail-Adresse" msgid "Sorry, registration is disabled on this instance." msgstr "Das Registrieren ist auf dieser Instanz leider deaktiviert." -#: mediagoblin/auth/views.py:73 +#: mediagoblin/auth/views.py:75 msgid "Sorry, a user with that name already exists." msgstr "Leider gibt es bereits einen Benutzer mit diesem Namen." -#: mediagoblin/auth/views.py:77 +#: mediagoblin/auth/views.py:79 msgid "Sorry, a user with that email address already exists." msgstr "Leider gibt es bereits einen Benutzer mit dieser E-Mail-Adresse." -#: mediagoblin/auth/views.py:180 +#: mediagoblin/auth/views.py:182 msgid "" "Your email address has been verified. You may now login, edit your profile, " "and submit images!" -msgstr "" -"Deine E-Mail-Adresse wurde bestätigt. Du kannst dich nun anmelden, Dein " -"Profil bearbeiten und Bilder hochladen!" +msgstr "Deine E-Mail-Adresse wurde bestätigt. Du kannst dich nun anmelden, Dein Profil bearbeiten und Bilder hochladen!" -#: mediagoblin/auth/views.py:186 +#: mediagoblin/auth/views.py:188 msgid "The verification key or user id is incorrect" msgstr "Der Bestätigungsschlüssel oder die Nutzernummer ist falsch." -#: mediagoblin/auth/views.py:204 +#: mediagoblin/auth/views.py:206 msgid "You must be logged in so we know who to send the email to!" msgstr "Du musst angemeldet sein, damit wir wissen, wer die Email bekommt." -#: mediagoblin/auth/views.py:212 +#: mediagoblin/auth/views.py:214 msgid "You've already verified your email address!" msgstr "Deine E-Mail-Adresse wurde bereits bestätigt." -#: mediagoblin/auth/views.py:225 +#: mediagoblin/auth/views.py:227 msgid "Resent your verification email." msgstr "Bestätigungs-E-Mail wurde erneut versandt." -#: mediagoblin/auth/views.py:260 +#: mediagoblin/auth/views.py:262 msgid "" "An email has been sent with instructions on how to change your password." -msgstr "" -"Es wurde eine Email mit Anweisungen für die Änderung des Passwortes an dich " -"gesendet." +msgstr "Es wurde eine Email mit Anweisungen für die Änderung des Passwortes an dich gesendet." -#: mediagoblin/auth/views.py:270 +#: mediagoblin/auth/views.py:272 msgid "" "Could not send password recovery email as your username is inactive or your " "account's email address has not been verified." -msgstr "" -"E-Mail zur Wiederherstellung des Passworts konnte nicht gesendet werden, " -"weil dein Benutzername inaktiv oder deine E-Mail-Adresse noch nicht " -"verifiziert ist." +msgstr "E-Mail zur Wiederherstellung des Passworts konnte nicht gesendet werden, weil dein Benutzername inaktiv oder deine E-Mail-Adresse noch nicht verifiziert ist." -#: mediagoblin/auth/views.py:282 +#: mediagoblin/auth/views.py:284 msgid "Couldn't find someone with that username or email." msgstr "Es konnte niemand mit diesem Nutzernamen oder Email gefunden werden." -#: mediagoblin/auth/views.py:330 +#: mediagoblin/auth/views.py:332 msgid "You can now log in using your new password." msgstr "Du kannst dich jetzt mit deinem neuen Passwort anmelden." @@ -118,10 +111,7 @@ msgid "" "You can use\n" " <a href=\"http://daringfireball.net/projects/markdown/basics\">\n" " Markdown</a> for formatting." -msgstr "" -"Für Formatierung kannst du\n" -" <a href=\"http://daringfireball.net/projects/markdown/basics\">\n" -" Markdown</a> benutzen." +msgstr "Für Formatierung kannst du\n <a href=\"http://daringfireball.net/projects/markdown/basics\">\n Markdown</a> benutzen." #: mediagoblin/edit/forms.py:33 mediagoblin/submit/forms.py:36 msgid "Tags" @@ -143,11 +133,10 @@ msgstr "Bitte gib einen Kurztitel ein" msgid "" "The title part of this media's address. You usually don't need to change " "this." -msgstr "" -"Der Titelteil der Medienadresse. Normalerweise muss hier nichts geändert " -"werden." +msgstr "Der Titelteil der Medienadresse. Normalerweise muss hier nichts geändert werden." #: mediagoblin/edit/forms.py:44 mediagoblin/submit/forms.py:41 +#: mediagoblin/templates/mediagoblin/utils/license.html:20 msgid "License" msgstr "Lizenz" @@ -165,34 +154,33 @@ msgstr "Altes Passwort" #: mediagoblin/edit/forms.py:65 msgid "Enter your old password to prove you own this account." -msgstr "" -"Gib dein altes Passwort ein, um zu bestätigen dass du dieses Konto besitzt." +msgstr "Gib dein altes Passwort ein, um zu bestätigen dass du dieses Konto besitzt." #: mediagoblin/edit/forms.py:68 msgid "New password" msgstr "Neues Passwort" -#: mediagoblin/edit/views.py:68 +#: mediagoblin/edit/views.py:67 msgid "An entry with that slug already exists for this user." msgstr "Diesen Kurztitel hast du bereits vergeben." -#: mediagoblin/edit/views.py:92 +#: mediagoblin/edit/views.py:88 msgid "You are editing another user's media. Proceed with caution." msgstr "Du bearbeitest die Medien eines Anderen. Bitte sei vorsichtig." -#: mediagoblin/edit/views.py:162 +#: mediagoblin/edit/views.py:158 msgid "You are editing a user's profile. Proceed with caution." msgstr "Du bearbeitest das Profil eines Anderen. Bitte sei vorsichtig." -#: mediagoblin/edit/views.py:180 +#: mediagoblin/edit/views.py:174 msgid "Profile changes saved" msgstr "Das Profil wurde aktualisiert" -#: mediagoblin/edit/views.py:206 +#: mediagoblin/edit/views.py:200 msgid "Wrong password" msgstr "Falsches Passwort" -#: mediagoblin/edit/views.py:222 +#: mediagoblin/edit/views.py:216 msgid "Account settings saved" msgstr "Kontoeinstellungen gespeichert" @@ -212,7 +200,7 @@ msgstr "Datei" msgid "You must provide a file." msgstr "Du musst eine Datei angeben." -#: mediagoblin/submit/views.py:158 +#: mediagoblin/submit/views.py:156 msgid "Woohoo! Submitted!" msgstr "Yeeeaaah! Geschafft!" @@ -232,9 +220,7 @@ msgstr "Tut uns Leid, aber unter der angegebenen Adresse gibt es keine Seite!" msgid "" "If you're sure the address is correct, maybe the page you're looking for has" " been moved or deleted." -msgstr "" -"Wenn du sicher bist, dass die Adresse stimmt, wurde die Seite eventuell " -"verschoben oder gelöscht." +msgstr "Wenn du sicher bist, dass die Adresse stimmt, wurde die Seite eventuell verschoben oder gelöscht." #: mediagoblin/templates/mediagoblin/base.html:46 msgid "MediaGoblin logo" @@ -262,10 +248,16 @@ msgstr "Anmelden" #: mediagoblin/templates/mediagoblin/base.html:84 msgid "" "Powered by <a href=\"http://mediagoblin.org\">MediaGoblin</a>, a <a " -"href=\"http://gnu.org/\">GNU</a> project" +"href=\"http://gnu.org/\">GNU</a> project." +msgstr "" + +#: mediagoblin/templates/mediagoblin/base.html:87 +#, python-format +msgid "" +"Released under the <a " +"href=\"http://www.fsf.org/licensing/licenses/agpl-3.0.html\">AGPL</a>. <a " +"href=\"%(source_link)s\">Source code</a> available." msgstr "" -"Läuft mit <a href=\"http://mediagoblin.org/\">MediaGoblin</a>, einem <a " -"href=\"http://gnu.org/\">GNU</a>-Projekt" #: mediagoblin/templates/mediagoblin/root.html:24 msgid "Explore" @@ -279,17 +271,13 @@ msgstr "Hallo du, willkommen auf dieser MediaGoblin-Seite!" msgid "" "This site is running <a href=\"http://mediagoblin.org\">MediaGoblin</a>, an " "extraordinarily great piece of media hosting software." -msgstr "" -"Diese Seite läuft mit <a href=\"http://mediagoblin.org\">MediaGoblin</a>, " -"einer großartigen Software für Medienhosting." +msgstr "Diese Seite läuft mit <a href=\"http://mediagoblin.org\">MediaGoblin</a>, einer großartigen Software für Medienhosting." #: mediagoblin/templates/mediagoblin/root.html:29 msgid "" "To add your own media, place comments, save your favourites and more, you " "can log in with your MediaGoblin account." -msgstr "" -"Melde dich mit deinem MediaGoblin-Konto an, um eigene Medien hinzuzufügen, " -"zu kommentieren, Favoriten zu speichern und mehr." +msgstr "Melde dich mit deinem MediaGoblin-Konto an, um eigene Medien hinzuzufügen, zu kommentieren, Favoriten zu speichern und mehr." #: mediagoblin/templates/mediagoblin/root.html:31 msgid "Don't have one yet? It's easy!" @@ -301,10 +289,7 @@ msgid "" "<a class=\"button_action_highlight\" href=\"%(register_url)s\">Create an account at this site</a>\n" " or\n" " <a class=\"button_action\" href=\"http://wiki.mediagoblin.org/HackingHowto\">Set up MediaGoblin on your own server</a>" -msgstr "" -"<a class=\"button_action_highlight\" href=\"%(register_url)s\">Registriere dich auf dieser Seite</a>\n" -" oder\n" -" <a class=\"button_action\" href=\"http://wiki.mediagoblin.org/HackingHowto\">Installiere MediaGoblin auf deinem eigenen Server</a>" +msgstr "<a class=\"button_action_highlight\" href=\"%(register_url)s\">Registriere dich auf dieser Seite</a>\n oder\n <a class=\"button_action\" href=\"http://wiki.mediagoblin.org/HackingHowto\">Installiere MediaGoblin auf deinem eigenen Server</a>" #: mediagoblin/templates/mediagoblin/root.html:40 msgid "Most recent media" @@ -338,14 +323,7 @@ msgid "" "\n" "If you think this is an error, just ignore this email and continue being\n" "a happy goblin!" -msgstr "" -"Hi %(username)s,\n" -"\n" -"um dein GNU MediaGoblin Passwort zu ändern, öffne folgende URL in deinem Webbrowser:\n" -"\n" -"%(verification_url)s\n" -"\n" -"Wenn du denkst, dass das ein Fehler ist, ignoriere einfach diese E-Mail und bleib ein glücklicher Goblin!" +msgstr "Hi %(username)s,\n\num dein GNU MediaGoblin Passwort zu ändern, öffne folgende URL in deinem Webbrowser:\n\n%(verification_url)s\n\nWenn du denkst, dass das ein Fehler ist, ignoriere einfach diese E-Mail und bleib ein glücklicher Goblin!" #: mediagoblin/templates/mediagoblin/auth/login.html:30 msgid "Logging in failed!" @@ -380,12 +358,7 @@ msgid "" "your web browser:\n" "\n" "%(verification_url)s" -msgstr "" -"Hallo %(username)s,\n" -"\n" -"um dein Konto bei GNU MediaGoblin zu aktivieren, musst du folgende Adresse in deinem Webbrowser öffnen:\n" -"\n" -"%(verification_url)s" +msgstr "Hallo %(username)s,\n\num dein Konto bei GNU MediaGoblin zu aktivieren, musst du folgende Adresse in deinem Webbrowser öffnen:\n\n%(verification_url)s" #: mediagoblin/templates/mediagoblin/edit/edit.html:29 #, python-format @@ -406,7 +379,7 @@ msgstr "Änderungen speichern" #: mediagoblin/templates/mediagoblin/edit/edit_account.html:34 #, python-format msgid "Changing %(username)s's account settings" -msgstr "%(username)s's Kontoeinstellungen werden geändert" +msgstr "%(username)ss Kontoeinstellungen werden geändert" #: mediagoblin/templates/mediagoblin/edit/edit_profile.html:29 #, python-format @@ -420,29 +393,23 @@ msgid "Media tagged with: %(tag_name)s" msgstr "Medien mit Schlagwort: %(tag_name)s" #: mediagoblin/templates/mediagoblin/media_displays/ascii.html:34 -#: mediagoblin/templates/mediagoblin/media_displays/video.html:46 +#: mediagoblin/templates/mediagoblin/media_displays/video.html:53 msgid "Original" msgstr "Original" -#: mediagoblin/templates/mediagoblin/media_displays/video.html:33 +#: mediagoblin/templates/mediagoblin/media_displays/video.html:40 msgid "" "Sorry, this video will not work because \n" "\t your web browser does not support HTML5 \n" "\t video." -msgstr "" -"Entschuldige, dieses Video wird nicht funktionieren weil \n" -"<span class=\"whitespace other\" title=\"Tab\">»</span> dein Webbrowser kein HTML5 \n" -"<span class=\"whitespace other\" title=\"Tab\">»</span> Video unterstützt." +msgstr "Entschuldige, dieses Video wird nicht funktionieren weil \n dein Webbrowser kein HTML5 \n Video unterstützt." -#: mediagoblin/templates/mediagoblin/media_displays/video.html:36 +#: mediagoblin/templates/mediagoblin/media_displays/video.html:43 msgid "" "You can get a modern web browser that \n" "\t can play this video at <a href=\"http://getfirefox.com\">\n" "\t http://getfirefox.com</a>!" -msgstr "" -"Hol dir einen modernen Webbrowser, der \n" -"<span class=\"whitespace other\" title=\"Tab\">»</span> dieses Video abspielen kann, <a href=\"http://getfirefox.com\">\n" -"<span class=\"whitespace other\" title=\"Tab\">»</span> Firefox</a>!" +msgstr "Hol dir einen modernen Webbrowser, der \n dieses Video abspielen kann, <a href=\"http://getfirefox.com\">\n Firefox</a>!" #: mediagoblin/templates/mediagoblin/submit/start.html:26 msgid "Add your media" @@ -462,59 +429,44 @@ msgstr "%(username)ss Medien" msgid "<a href=\"%(user_url)s\">%(username)s</a>'s media" msgstr "<a href=\"%(user_url)s\">%(username)s</a>s Medien" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:72 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:46 #, python-format -msgid "Added on %(date)s." -msgstr "Hinzugefügt am %(date)s." +msgid "â– Browsing media by <a href=\"%(user_url)s\">%(username)s</a>" +msgstr "" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:81 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:85 msgid "Edit" msgstr "Bearbeiten" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:85 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:89 msgid "Delete" msgstr "Löschen" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:91 -#, python-format -msgid "%(comment_count)s comment" -msgstr "%(comment_count)s Kommentar" - -#: mediagoblin/templates/mediagoblin/user_pages/media.html:93 -#, python-format -msgid "%(comment_count)s comments" -msgstr "%(comment_count)s Kommentare" - -#: mediagoblin/templates/mediagoblin/user_pages/media.html:95 -msgid "No comments yet." -msgstr "Bisher keine Kommentare." - -#: mediagoblin/templates/mediagoblin/user_pages/media.html:103 -msgid "Add one" -msgstr "Kommentiere etwas" +#: mediagoblin/templates/mediagoblin/user_pages/media.html:124 +msgid "Add a comment" +msgstr "" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:112 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:133 msgid "" "You can use <a " "href=\"http://daringfireball.net/projects/markdown/basics\">Markdown</a> for" " formatting." -msgstr "" -"Für Formatierung kannst du <a " -"href=\"http://daringfireball.net/projects/markdown/basics\">Markdown</a> " -"benutzen." +msgstr "Für Formatierung kannst du <a href=\"http://daringfireball.net/projects/markdown/basics\">Markdown</a> benutzen." -#: mediagoblin/templates/mediagoblin/user_pages/media.html:116 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:137 msgid "Add this comment" msgstr "Kommentar absenden" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:138 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:159 msgid "at" msgstr "bei" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:153 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:174 #, python-format -msgid "<p>â– Browsing media by <a href=\"%(user_url)s\">%(username)s</a></p>" -msgstr "<p>â– Medien von <a href=\"%(user_url)s\">%(username)s</a></p>" +msgid "" +"<h3>Added on</h3>\n" +" <p>%(date)s</p>" +msgstr "" #: mediagoblin/templates/mediagoblin/user_pages/media_confirm_delete.html:30 #, python-format @@ -522,8 +474,8 @@ msgid "Really delete %(title)s?" msgstr "%(title)s wirklich löschen?" #: mediagoblin/templates/mediagoblin/user_pages/media_confirm_delete.html:50 -msgid "Delete Permanently" -msgstr "Dauerhaft löschen." +msgid "Delete permanently" +msgstr "" #: mediagoblin/templates/mediagoblin/user_pages/processing_panel.html:22 msgid "Media processing panel" @@ -532,9 +484,7 @@ msgstr "Medienverarbeitung" #: mediagoblin/templates/mediagoblin/user_pages/processing_panel.html:25 msgid "" "You can track the state of media being processed for your gallery here." -msgstr "" -"Du kannst den Status der gerade in Bearbeitung befindlichen Medien hier " -"betrachten." +msgstr "Du kannst den Status der gerade in Bearbeitung befindlichen Medien hier betrachten." #: mediagoblin/templates/mediagoblin/user_pages/processing_panel.html:28 msgid "Media in-processing" @@ -570,9 +520,7 @@ msgstr "Fast fertig! Dein Konto muss noch freigeschaltet werden." #: mediagoblin/templates/mediagoblin/user_pages/user.html:58 msgid "" "An email should arrive in a few moments with instructions on how to do so." -msgstr "" -"Gleich solltest du eine E-Mail erhalten, die dir erklärt, was du noch machen" -" musst." +msgstr "Gleich solltest du eine E-Mail erhalten, die dir erklärt, was du noch machen musst." #: mediagoblin/templates/mediagoblin/user_pages/user.html:62 msgid "In case it doesn't:" @@ -586,19 +534,14 @@ msgstr "Bestätigung erneut senden" msgid "" "Someone has registered an account with this username, but it still has to be" " activated." -msgstr "" -"Jemand hat bereits ein Konto mit diesem Benutzernamen registriert, aber es " -"muss noch aktiviert werden." +msgstr "Jemand hat bereits ein Konto mit diesem Benutzernamen registriert, aber es muss noch aktiviert werden." #: mediagoblin/templates/mediagoblin/user_pages/user.html:79 #, python-format msgid "" "If you are that person but you've lost your verification email, you can <a " "href=\"%(login_url)s\">log in</a> and resend it." -msgstr "" -"Wenn dir dieses Konto gehört und die Bestätigungsmail verloren gegangen ist," -" kannst du dich <a href=\"%(login_url)s\">anmelden</a> und sie erneut " -"senden." +msgstr "Wenn dir dieses Konto gehört und die Bestätigungsmail verloren gegangen ist, kannst du dich <a href=\"%(login_url)s\">anmelden</a> und sie erneut senden." #: mediagoblin/templates/mediagoblin/user_pages/user.html:96 msgid "Here's a spot to tell others about yourself." @@ -641,9 +584,14 @@ msgstr "Feed-Symbol" msgid "Atom feed" msgstr "Atom-Feed" -#: mediagoblin/templates/mediagoblin/utils/license.html:21 -msgid "License:" -msgstr "Lizenz:" +#: mediagoblin/templates/mediagoblin/utils/geolocation_map.html:25 +msgid "Location" +msgstr "" + +#: mediagoblin/templates/mediagoblin/utils/geolocation_map.html:38 +#, python-format +msgid "View on <a href=\"%(osm_url)s\">OpenStreetMap</a>" +msgstr "" #: mediagoblin/templates/mediagoblin/utils/license.html:25 msgid "All rights reserved" @@ -661,23 +609,19 @@ msgstr "Ältere →" msgid "Go to page:" msgstr "Zu Seite:" -#: mediagoblin/templates/mediagoblin/utils/prev_next.html:27 -#: mediagoblin/templates/mediagoblin/utils/prev_next.html:32 +#: mediagoblin/templates/mediagoblin/utils/prev_next.html:28 +#: mediagoblin/templates/mediagoblin/utils/prev_next.html:33 msgid "newer" msgstr "neuer" -#: mediagoblin/templates/mediagoblin/utils/prev_next.html:38 -#: mediagoblin/templates/mediagoblin/utils/prev_next.html:43 +#: mediagoblin/templates/mediagoblin/utils/prev_next.html:39 +#: mediagoblin/templates/mediagoblin/utils/prev_next.html:44 msgid "older" msgstr "älter" #: mediagoblin/templates/mediagoblin/utils/tags.html:20 -msgid "View more media tagged with" -msgstr "Mehr Medien anschauen mit dem Schlagwort" - -#: mediagoblin/templates/mediagoblin/utils/tags.html:25 -msgid "or" -msgstr "oder" +msgid "Tagged with" +msgstr "" #: mediagoblin/tools/exif.py:68 msgid "Could not read the image file." @@ -687,26 +631,22 @@ msgstr "Die Bilddatei konnte nicht gelesen werden." msgid "I am sure I want to delete this" msgstr "Ja, wirklich löschen" -#: mediagoblin/user_pages/views.py:155 +#: mediagoblin/user_pages/views.py:153 msgid "Oops, your comment was empty." msgstr "Ohh, der Kommentar war leer." -#: mediagoblin/user_pages/views.py:161 +#: mediagoblin/user_pages/views.py:159 msgid "Your comment has been posted!" msgstr "Dein Kommentar wurde gesendet!" -#: mediagoblin/user_pages/views.py:183 +#: mediagoblin/user_pages/views.py:185 msgid "You deleted the media." msgstr "Du hast das Medium gelöscht." -#: mediagoblin/user_pages/views.py:190 +#: mediagoblin/user_pages/views.py:192 msgid "The media was not deleted because you didn't check that you were sure." -msgstr "" -"Das Medium wurde nicht gelöscht. Du musst ankreuzen, dass du es wirklich " -"löschen möchtest." +msgstr "Das Medium wurde nicht gelöscht. Du musst ankreuzen, dass du es wirklich löschen möchtest." -#: mediagoblin/user_pages/views.py:198 +#: mediagoblin/user_pages/views.py:200 msgid "You are about to delete another user's media. Proceed with caution." msgstr "Du versuchst Medien eines anderen Nutzers zu löschen. Sei vorsichtig." - - diff --git a/mediagoblin/i18n/en/LC_MESSAGES/mediagoblin.po b/mediagoblin/i18n/en/LC_MESSAGES/mediagoblin.po index b5832fe4..d91bbab6 100644 --- a/mediagoblin/i18n/en/LC_MESSAGES/mediagoblin.po +++ b/mediagoblin/i18n/en/LC_MESSAGES/mediagoblin.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2012-02-09 09:30-0600\n" +"POT-Creation-Date: 2012-03-18 15:16-0500\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Language-Team: LANGUAGE <LL@li.org>\n" @@ -17,7 +17,7 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Generated-By: Babel 0.9.6\n" -#: mediagoblin/processing.py:153 +#: mediagoblin/processing.py:154 msgid "Invalid file given for media type." msgstr "" @@ -37,51 +37,51 @@ msgstr "" msgid "Sorry, registration is disabled on this instance." msgstr "" -#: mediagoblin/auth/views.py:73 +#: mediagoblin/auth/views.py:75 msgid "Sorry, a user with that name already exists." msgstr "" -#: mediagoblin/auth/views.py:77 +#: mediagoblin/auth/views.py:79 msgid "Sorry, a user with that email address already exists." msgstr "" -#: mediagoblin/auth/views.py:180 +#: mediagoblin/auth/views.py:182 msgid "" "Your email address has been verified. You may now login, edit your " "profile, and submit images!" msgstr "" -#: mediagoblin/auth/views.py:186 +#: mediagoblin/auth/views.py:188 msgid "The verification key or user id is incorrect" msgstr "" -#: mediagoblin/auth/views.py:204 +#: mediagoblin/auth/views.py:206 msgid "You must be logged in so we know who to send the email to!" msgstr "" -#: mediagoblin/auth/views.py:212 +#: mediagoblin/auth/views.py:214 msgid "You've already verified your email address!" msgstr "" -#: mediagoblin/auth/views.py:225 +#: mediagoblin/auth/views.py:227 msgid "Resent your verification email." msgstr "" -#: mediagoblin/auth/views.py:260 +#: mediagoblin/auth/views.py:262 msgid "An email has been sent with instructions on how to change your password." msgstr "" -#: mediagoblin/auth/views.py:270 +#: mediagoblin/auth/views.py:272 msgid "" "Could not send password recovery email as your username is inactive or " "your account's email address has not been verified." msgstr "" -#: mediagoblin/auth/views.py:282 +#: mediagoblin/auth/views.py:284 msgid "Couldn't find someone with that username or email." msgstr "" -#: mediagoblin/auth/views.py:330 +#: mediagoblin/auth/views.py:332 msgid "You can now log in using your new password." msgstr "" @@ -125,6 +125,7 @@ msgid "" msgstr "" #: mediagoblin/edit/forms.py:44 mediagoblin/submit/forms.py:41 +#: mediagoblin/templates/mediagoblin/utils/license.html:20 msgid "License" msgstr "" @@ -148,27 +149,27 @@ msgstr "" msgid "New password" msgstr "" -#: mediagoblin/edit/views.py:68 +#: mediagoblin/edit/views.py:67 msgid "An entry with that slug already exists for this user." msgstr "" -#: mediagoblin/edit/views.py:92 +#: mediagoblin/edit/views.py:88 msgid "You are editing another user's media. Proceed with caution." msgstr "" -#: mediagoblin/edit/views.py:162 +#: mediagoblin/edit/views.py:158 msgid "You are editing a user's profile. Proceed with caution." msgstr "" -#: mediagoblin/edit/views.py:180 +#: mediagoblin/edit/views.py:174 msgid "Profile changes saved" msgstr "" -#: mediagoblin/edit/views.py:206 +#: mediagoblin/edit/views.py:200 msgid "Wrong password" msgstr "" -#: mediagoblin/edit/views.py:222 +#: mediagoblin/edit/views.py:216 msgid "Account settings saved" msgstr "" @@ -188,7 +189,7 @@ msgstr "" msgid "You must provide a file." msgstr "" -#: mediagoblin/submit/views.py:158 +#: mediagoblin/submit/views.py:156 msgid "Woohoo! Submitted!" msgstr "" @@ -236,7 +237,15 @@ msgstr "" #: mediagoblin/templates/mediagoblin/base.html:84 msgid "" "Powered by <a href=\"http://mediagoblin.org\">MediaGoblin</a>, a <a " -"href=\"http://gnu.org/\">GNU</a> project" +"href=\"http://gnu.org/\">GNU</a> project." +msgstr "" + +#: mediagoblin/templates/mediagoblin/base.html:87 +#, python-format +msgid "" +"Released under the <a " +"href=\"http://www.fsf.org/licensing/licenses/agpl-3.0.html\">AGPL</a>. <a" +" href=\"%(source_link)s\">Source code</a> available." msgstr "" #: mediagoblin/templates/mediagoblin/root.html:24 @@ -376,18 +385,18 @@ msgid "Media tagged with: %(tag_name)s" msgstr "" #: mediagoblin/templates/mediagoblin/media_displays/ascii.html:34 -#: mediagoblin/templates/mediagoblin/media_displays/video.html:46 +#: mediagoblin/templates/mediagoblin/media_displays/video.html:53 msgid "Original" msgstr "" -#: mediagoblin/templates/mediagoblin/media_displays/video.html:33 +#: mediagoblin/templates/mediagoblin/media_displays/video.html:40 msgid "" "Sorry, this video will not work because \n" "\t your web browser does not support HTML5 \n" "\t video." msgstr "" -#: mediagoblin/templates/mediagoblin/media_displays/video.html:36 +#: mediagoblin/templates/mediagoblin/media_displays/video.html:43 msgid "" "You can get a modern web browser that \n" "\t can play this video at <a href=\"http://getfirefox.com\">\n" @@ -412,55 +421,43 @@ msgstr "" msgid "<a href=\"%(user_url)s\">%(username)s</a>'s media" msgstr "" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:72 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:46 #, python-format -msgid "Added on %(date)s." +msgid "â– Browsing media by <a href=\"%(user_url)s\">%(username)s</a>" msgstr "" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:81 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:85 msgid "Edit" msgstr "" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:85 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:89 msgid "Delete" msgstr "" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:91 -#, python-format -msgid "%(comment_count)s comment" -msgstr "" - -#: mediagoblin/templates/mediagoblin/user_pages/media.html:93 -#, python-format -msgid "%(comment_count)s comments" -msgstr "" - -#: mediagoblin/templates/mediagoblin/user_pages/media.html:95 -msgid "No comments yet." +#: mediagoblin/templates/mediagoblin/user_pages/media.html:124 +msgid "Add a comment" msgstr "" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:103 -msgid "Add one" -msgstr "" - -#: mediagoblin/templates/mediagoblin/user_pages/media.html:112 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:133 msgid "" "You can use <a " "href=\"http://daringfireball.net/projects/markdown/basics\">Markdown</a> " "for formatting." msgstr "" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:116 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:137 msgid "Add this comment" msgstr "" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:138 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:159 msgid "at" msgstr "" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:153 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:174 #, python-format -msgid "<p>â– Browsing media by <a href=\"%(user_url)s\">%(username)s</a></p>" +msgid "" +"<h3>Added on</h3>\n" +" <p>%(date)s</p>" msgstr "" #: mediagoblin/templates/mediagoblin/user_pages/media_confirm_delete.html:30 @@ -469,7 +466,7 @@ msgid "Really delete %(title)s?" msgstr "" #: mediagoblin/templates/mediagoblin/user_pages/media_confirm_delete.html:50 -msgid "Delete Permanently" +msgid "Delete permanently" msgstr "" #: mediagoblin/templates/mediagoblin/user_pages/processing_panel.html:22 @@ -577,8 +574,13 @@ msgstr "" msgid "Atom feed" msgstr "" -#: mediagoblin/templates/mediagoblin/utils/license.html:21 -msgid "License:" +#: mediagoblin/templates/mediagoblin/utils/geolocation_map.html:25 +msgid "Location" +msgstr "" + +#: mediagoblin/templates/mediagoblin/utils/geolocation_map.html:38 +#, python-format +msgid "View on <a href=\"%(osm_url)s\">OpenStreetMap</a>" msgstr "" #: mediagoblin/templates/mediagoblin/utils/license.html:25 @@ -597,22 +599,18 @@ msgstr "" msgid "Go to page:" msgstr "" -#: mediagoblin/templates/mediagoblin/utils/prev_next.html:27 -#: mediagoblin/templates/mediagoblin/utils/prev_next.html:32 +#: mediagoblin/templates/mediagoblin/utils/prev_next.html:28 +#: mediagoblin/templates/mediagoblin/utils/prev_next.html:33 msgid "newer" msgstr "" -#: mediagoblin/templates/mediagoblin/utils/prev_next.html:38 -#: mediagoblin/templates/mediagoblin/utils/prev_next.html:43 +#: mediagoblin/templates/mediagoblin/utils/prev_next.html:39 +#: mediagoblin/templates/mediagoblin/utils/prev_next.html:44 msgid "older" msgstr "" #: mediagoblin/templates/mediagoblin/utils/tags.html:20 -msgid "View more media tagged with" -msgstr "" - -#: mediagoblin/templates/mediagoblin/utils/tags.html:25 -msgid "or" +msgid "Tagged with" msgstr "" #: mediagoblin/tools/exif.py:68 @@ -623,23 +621,23 @@ msgstr "" msgid "I am sure I want to delete this" msgstr "" -#: mediagoblin/user_pages/views.py:155 +#: mediagoblin/user_pages/views.py:153 msgid "Oops, your comment was empty." msgstr "" -#: mediagoblin/user_pages/views.py:161 +#: mediagoblin/user_pages/views.py:159 msgid "Your comment has been posted!" msgstr "" -#: mediagoblin/user_pages/views.py:183 +#: mediagoblin/user_pages/views.py:185 msgid "You deleted the media." msgstr "" -#: mediagoblin/user_pages/views.py:190 +#: mediagoblin/user_pages/views.py:192 msgid "The media was not deleted because you didn't check that you were sure." msgstr "" -#: mediagoblin/user_pages/views.py:198 +#: mediagoblin/user_pages/views.py:200 msgid "You are about to delete another user's media. Proceed with caution." msgstr "" diff --git a/mediagoblin/i18n/eo/LC_MESSAGES/mediagoblin.mo b/mediagoblin/i18n/eo/LC_MESSAGES/mediagoblin.mo Binary files differindex 25ab5836..bc615a92 100644 --- a/mediagoblin/i18n/eo/LC_MESSAGES/mediagoblin.mo +++ b/mediagoblin/i18n/eo/LC_MESSAGES/mediagoblin.mo diff --git a/mediagoblin/i18n/eo/LC_MESSAGES/mediagoblin.po b/mediagoblin/i18n/eo/LC_MESSAGES/mediagoblin.po index 49626556..a929d727 100644 --- a/mediagoblin/i18n/eo/LC_MESSAGES/mediagoblin.po +++ b/mediagoblin/i18n/eo/LC_MESSAGES/mediagoblin.po @@ -10,9 +10,9 @@ msgid "" msgstr "" "Project-Id-Version: GNU MediaGoblin\n" "Report-Msgid-Bugs-To: http://issues.mediagoblin.org/\n" -"POT-Creation-Date: 2012-01-29 13:47-0600\n" -"PO-Revision-Date: 2012-02-05 21:07+0000\n" -"Last-Translator: aleksejrs <deletesoftware@yandex.ru>\n" +"POT-Creation-Date: 2012-03-18 15:16-0500\n" +"PO-Revision-Date: 2012-03-18 20:14+0000\n" +"Last-Translator: cwebber <cwebber@dustycloud.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -21,7 +21,7 @@ msgstr "" "Language: eo\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" -#: mediagoblin/processing.py:143 +#: mediagoblin/processing.py:154 msgid "Invalid file given for media type." msgstr "La provizita dosiero ne konformas al la informtipo." @@ -41,56 +41,52 @@ msgstr "RetpoÅtadreso" msgid "Sorry, registration is disabled on this instance." msgstr "BedaÅrinde, registrado estas malaktivigita en tiu ĉi instalaĵo." -#: mediagoblin/auth/views.py:73 +#: mediagoblin/auth/views.py:75 msgid "Sorry, a user with that name already exists." msgstr "BedaÅrinde, uzanto kun tiu nomo jam ekzistas." -#: mediagoblin/auth/views.py:77 +#: mediagoblin/auth/views.py:79 msgid "Sorry, a user with that email address already exists." msgstr "Ni bedaÅras, sed konto kun tiu retpoÅtadreso jam ekzistas." -#: mediagoblin/auth/views.py:180 +#: mediagoblin/auth/views.py:182 msgid "" "Your email address has been verified. You may now login, edit your profile, " "and submit images!" -msgstr "" -"Via retpoÅtadreso estas konfirmita. Vi povas nun ensaluti, redakti vian " -"profilon, kaj alÅuti bildojn!" +msgstr "Via retpoÅtadreso estas konfirmita. Vi povas nun ensaluti, redakti vian profilon, kaj alÅuti bildojn!" -#: mediagoblin/auth/views.py:186 +#: mediagoblin/auth/views.py:188 msgid "The verification key or user id is incorrect" msgstr "La kontrol-kodo aÅ la uzantonomo ne estas korekta" -#: mediagoblin/auth/views.py:204 +#: mediagoblin/auth/views.py:206 msgid "You must be logged in so we know who to send the email to!" msgstr "Vi devas esti ensalutita, por ke ni sciu, al kiu sendi la retleteron!" -#: mediagoblin/auth/views.py:212 +#: mediagoblin/auth/views.py:214 msgid "You've already verified your email address!" msgstr "Vi jam konfirmis vian retpoÅtadreson!" -#: mediagoblin/auth/views.py:225 +#: mediagoblin/auth/views.py:227 msgid "Resent your verification email." msgstr "Resendi vian kontrol-mesaÄon." -#: mediagoblin/auth/views.py:260 +#: mediagoblin/auth/views.py:262 msgid "" "An email has been sent with instructions on how to change your password." msgstr "Senditas retletero kun instrukcio pri kiel ÅanÄi vian pasvorton." -#: mediagoblin/auth/views.py:270 +#: mediagoblin/auth/views.py:272 msgid "" "Could not send password recovery email as your username is inactive or your " "account's email address has not been verified." -msgstr "" -"Ni ne povas sendi pasvortsavan retleteron, ĉar aÅ via konto estas neaktiva, " -"aÅ Äia retpoÅtadreso ne estis konfirmita." +msgstr "Ni ne povas sendi pasvortsavan retleteron, ĉar aÅ via konto estas neaktiva, aÅ Äia retpoÅtadreso ne estis konfirmita." -#: mediagoblin/auth/views.py:282 +#: mediagoblin/auth/views.py:284 msgid "Couldn't find someone with that username or email." msgstr "Mi trovis neniun kun tiu salutnomo aÅ retpoÅtadreso." -#: mediagoblin/auth/views.py:330 +#: mediagoblin/auth/views.py:332 msgid "You can now log in using your new password." msgstr "Nun vi povas ensaluti per via nova pasvorto." @@ -108,10 +104,7 @@ msgid "" "You can use\n" " <a href=\"http://daringfireball.net/projects/markdown/basics\">\n" " Markdown</a> for formatting." -msgstr "" -"Vi povas uzi por markado la lingvon\n" -" «<a href=\"http://daringfireball.net/projects/markdown/basics\">\n" -" Markdown</a>»." +msgstr "Vi povas uzi por markado la lingvon\n «<a href=\"http://daringfireball.net/projects/markdown/basics\">\n Markdown</a>»." #: mediagoblin/edit/forms.py:33 mediagoblin/submit/forms.py:36 msgid "Tags" @@ -133,11 +126,10 @@ msgstr "La distingiga adresparto ne povas esti malplena" msgid "" "The title part of this media's address. You usually don't need to change " "this." -msgstr "" -"La dosiertitol-bazita parto de la dosieradreso. Ordinare ne necesas Äin " -"ÅanÄi." +msgstr "La dosiertitol-bazita parto de la dosieradreso. Ordinare ne necesas Äin ÅanÄi." #: mediagoblin/edit/forms.py:44 mediagoblin/submit/forms.py:41 +#: mediagoblin/templates/mediagoblin/utils/license.html:20 msgid "License" msgstr "Permesilo" @@ -161,27 +153,27 @@ msgstr "Enigu vian malnovan pasvorton por pruvi, ke ĉi tiu konto estas via." msgid "New password" msgstr "La nova pasvorto" -#: mediagoblin/edit/views.py:68 +#: mediagoblin/edit/views.py:67 msgid "An entry with that slug already exists for this user." msgstr "Ĉi tiu uzanto jam havas dosieron kun tiu distingiga adresparto." -#: mediagoblin/edit/views.py:92 +#: mediagoblin/edit/views.py:88 msgid "You are editing another user's media. Proceed with caution." msgstr "Vi priredaktas dosieron de alia uzanto. Agu singardeme." -#: mediagoblin/edit/views.py:162 +#: mediagoblin/edit/views.py:158 msgid "You are editing a user's profile. Proceed with caution." msgstr "Vi redaktas profilon de alia uzanto. Agu singardeme." -#: mediagoblin/edit/views.py:180 +#: mediagoblin/edit/views.py:174 msgid "Profile changes saved" msgstr "ProfilÅanÄoj estis konservitaj" -#: mediagoblin/edit/views.py:206 +#: mediagoblin/edit/views.py:200 msgid "Wrong password" msgstr "MalÄusta pasvorto" -#: mediagoblin/edit/views.py:222 +#: mediagoblin/edit/views.py:216 msgid "Account settings saved" msgstr "Kontagordoj estis konservitaj" @@ -201,7 +193,7 @@ msgstr "Dosiero" msgid "You must provide a file." msgstr "Vi devas provizi dosieron." -#: mediagoblin/submit/views.py:158 +#: mediagoblin/submit/views.py:156 msgid "Woohoo! Submitted!" msgstr "Hura! AlÅutitas!" @@ -221,9 +213,7 @@ msgstr "VerÅajne ĉe ĉi tiu adreso ne estas paÄo. Ni bedaÅras!" msgid "" "If you're sure the address is correct, maybe the page you're looking for has" " been moved or deleted." -msgstr "" -"Se vi estas certa, ke la adreso estas Äusta, eble la serĉata de vi paÄo " -"estis movita aÅ forigita." +msgstr "Se vi estas certa, ke la adreso estas Äusta, eble la serĉata de vi paÄo estis movita aÅ forigita." #: mediagoblin/templates/mediagoblin/base.html:46 msgid "MediaGoblin logo" @@ -251,10 +241,16 @@ msgstr "Ensaluti" #: mediagoblin/templates/mediagoblin/base.html:84 msgid "" "Powered by <a href=\"http://mediagoblin.org\">MediaGoblin</a>, a <a " -"href=\"http://gnu.org/\">GNU</a> project" +"href=\"http://gnu.org/\">GNU</a> project." +msgstr "" + +#: mediagoblin/templates/mediagoblin/base.html:87 +#, python-format +msgid "" +"Released under the <a " +"href=\"http://www.fsf.org/licensing/licenses/agpl-3.0.html\">AGPL</a>. <a " +"href=\"%(source_link)s\">Source code</a> available." msgstr "" -"Funkcias per <a href=\"http://mediagoblin.org\">MediaGoblin</a>, unu el la " -"<a href=\"http://gnu.org/\">projektoj de GNU</a>" #: mediagoblin/templates/mediagoblin/root.html:24 msgid "Explore" @@ -268,18 +264,13 @@ msgstr "Saluton, kaj bonvenon al ĉi tiu MediaGoblina retpaÄaro!" msgid "" "This site is running <a href=\"http://mediagoblin.org\">MediaGoblin</a>, an " "extraordinarily great piece of media hosting software." -msgstr "" -"Ĉi tiu retpaÄaro funkcias per <a " -"href=\"http://mediagoblin.org\">MediaGoblin</a>, eksterordinare bonega " -"programaro por gastigado de aÅdâ€vidâ€dosieroj." +msgstr "Ĉi tiu retpaÄaro funkcias per <a href=\"http://mediagoblin.org\">MediaGoblin</a>, eksterordinare bonega programaro por gastigado de aÅdâ€vidâ€dosieroj." #: mediagoblin/templates/mediagoblin/root.html:29 msgid "" "To add your own media, place comments, save your favourites and more, you " "can log in with your MediaGoblin account." -msgstr "" -"Por aldoni viajn proprajn dosierojn, fari al vi liston de la plej plaĉaj, " -"ks, vi povas ensaluti je via MediaGoblina konto." +msgstr "Por aldoni viajn proprajn dosierojn, fari al vi liston de la plej plaĉaj, ks, vi povas ensaluti je via MediaGoblina konto." #: mediagoblin/templates/mediagoblin/root.html:31 msgid "Don't have one yet? It's easy!" @@ -291,10 +282,7 @@ msgid "" "<a class=\"button_action_highlight\" href=\"%(register_url)s\">Create an account at this site</a>\n" " or\n" " <a class=\"button_action\" href=\"http://wiki.mediagoblin.org/HackingHowto\">Set up MediaGoblin on your own server</a>" -msgstr "" -"<a class=\"button_action_highlight\" href=\"%(register_url)s\">Kreu konton en ĉi tiu retejo</a>\n" -" aÅ\n" -" <a class=\"button_action\" href=\"http://wiki.mediagoblin.org/HackingHowto\">ekfunkciigu MediaGoblin’on en via propra servilo</a>" +msgstr "<a class=\"button_action_highlight\" href=\"%(register_url)s\">Kreu konton en ĉi tiu retejo</a>\n aÅ\n <a class=\"button_action\" href=\"http://wiki.mediagoblin.org/HackingHowto\">ekfunkciigu MediaGoblin’on en via propra servilo</a>" #: mediagoblin/templates/mediagoblin/root.html:40 msgid "Most recent media" @@ -328,14 +316,7 @@ msgid "" "\n" "If you think this is an error, just ignore this email and continue being\n" "a happy goblin!" -msgstr "" -"Saluton, %(username)s,\n" -"\n" -"por ÅanÄi vian pasvorton ĉe GNUa MediaGoblin, sekvu la jenan retadreson per via TTT-legilo:\n" -"\n" -"%(verification_url)s\n" -"\n" -"Se vi pensas, ke ĉi tiu retletero estas sendita erare, simple ignoru Äin kaj plu restu feliĉa koboldo!" +msgstr "Saluton, %(username)s,\n\npor ÅanÄi vian pasvorton ĉe GNUa MediaGoblin, sekvu la jenan retadreson per via TTT-legilo:\n\n%(verification_url)s\n\nSe vi pensas, ke ĉi tiu retletero estas sendita erare, simple ignoru Äin kaj plu restu feliĉa koboldo!" #: mediagoblin/templates/mediagoblin/auth/login.html:30 msgid "Logging in failed!" @@ -370,12 +351,7 @@ msgid "" "your web browser:\n" "\n" "%(verification_url)s" -msgstr "" -"Sal %(username)s,\n" -"\n" -"por aktivigi vian GNU MediaGoblin konton, malfermu la sekvantan URLon en via retumilo:\n" -"\n" -"%(verification_url)s" +msgstr "Sal %(username)s,\n\npor aktivigi vian GNU MediaGoblin konton, malfermu la sekvantan URLon en via retumilo:\n\n%(verification_url)s" #: mediagoblin/templates/mediagoblin/edit/edit.html:29 #, python-format @@ -410,29 +386,23 @@ msgid "Media tagged with: %(tag_name)s" msgstr "Dosieroj kun etikedo: %(tag_name)s" #: mediagoblin/templates/mediagoblin/media_displays/ascii.html:34 -#: mediagoblin/templates/mediagoblin/media_displays/video.html:46 +#: mediagoblin/templates/mediagoblin/media_displays/video.html:53 msgid "Original" msgstr "Originalo" -#: mediagoblin/templates/mediagoblin/media_displays/video.html:33 +#: mediagoblin/templates/mediagoblin/media_displays/video.html:40 msgid "" "Sorry, this video will not work because \n" "\t your web browser does not support HTML5 \n" "\t video." -msgstr "" -"BedaÅrinde ĉi tiu filmo ne spekteblas, ĉar\n" -"<span class=\"whitespace other\" title=\"Tab\">»</span> via TTT-legilo ne subtenas montradon\n" -"<span class=\"whitespace other\" title=\"Tab\">»</span> de filmoj laÅ HTML5." +msgstr "BedaÅrinde ĉi tiu filmo ne spekteblas, ĉar\n<span class=\"whitespace other\" title=\"Tab\">»</span> via TTT-legilo ne subtenas montradon\n<span class=\"whitespace other\" title=\"Tab\">»</span> de filmoj laÅ HTML5." -#: mediagoblin/templates/mediagoblin/media_displays/video.html:36 +#: mediagoblin/templates/mediagoblin/media_displays/video.html:43 msgid "" "You can get a modern web browser that \n" "\t can play this video at <a href=\"http://getfirefox.com\">\n" "\t http://getfirefox.com</a>!" -msgstr "" -"Vi povas akiri modernan TTT-legilon,\n" -"<span class=\"whitespace other\" title=\"Tab\">»</span> kapablan montri ĉi tiun filmon, ĉe <a href=\"http://getfirefox.com\">\n" -"<span class=\"whitespace other\" title=\"Tab\">»</span> http://getfirefox.com</a>!" +msgstr "Vi povas akiri modernan TTT-legilon,\n<span class=\"whitespace other\" title=\"Tab\">»</span> kapablan montri ĉi tiun filmon, ĉe <a href=\"http://getfirefox.com\">\n<span class=\"whitespace other\" title=\"Tab\">»</span> http://getfirefox.com</a>!" #: mediagoblin/templates/mediagoblin/submit/start.html:26 msgid "Add your media" @@ -452,59 +422,44 @@ msgstr "Dosieroj de %(username)s" msgid "<a href=\"%(user_url)s\">%(username)s</a>'s media" msgstr "Dosieroj de <a href=\"%(user_url)s\">%(username)s</a>" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:72 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:46 #, python-format -msgid "Added on %(date)s." -msgstr "Aldonita je %(date)s." +msgid "â– Browsing media by <a href=\"%(user_url)s\">%(username)s</a>" +msgstr "" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:81 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:85 msgid "Edit" msgstr "ÅœanÄi" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:85 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:89 msgid "Delete" msgstr "Forigi" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:91 -#, python-format -msgid "%(comment_count)s comment" -msgstr "%(comment_count)s komento" - -#: mediagoblin/templates/mediagoblin/user_pages/media.html:93 -#, python-format -msgid "%(comment_count)s comments" -msgstr "%(comment_count)s komentoj" - -#: mediagoblin/templates/mediagoblin/user_pages/media.html:95 -msgid "No comments yet." -msgstr "Estas neniom da komentoj." - -#: mediagoblin/templates/mediagoblin/user_pages/media.html:103 -msgid "Add one" -msgstr "Komenti" +#: mediagoblin/templates/mediagoblin/user_pages/media.html:124 +msgid "Add a comment" +msgstr "" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:112 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:133 msgid "" "You can use <a " "href=\"http://daringfireball.net/projects/markdown/basics\">Markdown</a> for" " formatting." -msgstr "" -"Vi povas uzi por markado la lingvon «<a " -"href=\"http://daringfireball.net/projects/markdown/basics\">Markdown</a>»." +msgstr "Vi povas uzi por markado la lingvon «<a href=\"http://daringfireball.net/projects/markdown/basics\">Markdown</a>»." -#: mediagoblin/templates/mediagoblin/user_pages/media.html:116 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:137 msgid "Add this comment" msgstr "Aldoni ĉi tiun komenton" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:138 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:159 msgid "at" msgstr "je" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:153 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:174 #, python-format -msgid "<p>â– Browsing media by <a href=\"%(user_url)s\">%(username)s</a></p>" +msgid "" +"<h3>Added on</h3>\n" +" <p>%(date)s</p>" msgstr "" -"<p>â– Foliumado de dosieraro de <a href=\"%(user_url)s\">%(username)s</a></p>" #: mediagoblin/templates/mediagoblin/user_pages/media_confirm_delete.html:30 #, python-format @@ -512,8 +467,8 @@ msgid "Really delete %(title)s?" msgstr "Ĉu efektive forigi %(title)s?" #: mediagoblin/templates/mediagoblin/user_pages/media_confirm_delete.html:50 -msgid "Delete Permanently" -msgstr "Forigi senrevene" +msgid "Delete permanently" +msgstr "" #: mediagoblin/templates/mediagoblin/user_pages/processing_panel.html:22 msgid "Media processing panel" @@ -522,9 +477,7 @@ msgstr "Kontrolejo pri dosierpreparado." #: mediagoblin/templates/mediagoblin/user_pages/processing_panel.html:25 msgid "" "You can track the state of media being processed for your gallery here." -msgstr "" -"Ĉi tie vi povas informiÄi pri la stato de preparado de dosieroj por via " -"galerio." +msgstr "Ĉi tie vi povas informiÄi pri la stato de preparado de dosieroj por via galerio." #: mediagoblin/templates/mediagoblin/user_pages/processing_panel.html:28 msgid "Media in-processing" @@ -560,8 +513,7 @@ msgstr "PreskaÅ finite! Restas nur validigi vian konton." #: mediagoblin/templates/mediagoblin/user_pages/user.html:58 msgid "" "An email should arrive in a few moments with instructions on how to do so." -msgstr "" -"Post kelkaj momentoj devas veni retletero kun instrukcio pri kiel tion fari." +msgstr "Post kelkaj momentoj devas veni retletero kun instrukcio pri kiel tion fari." #: mediagoblin/templates/mediagoblin/user_pages/user.html:62 msgid "In case it doesn't:" @@ -575,18 +527,14 @@ msgstr "Resendi kontrolmesaÄon" msgid "" "Someone has registered an account with this username, but it still has to be" " activated." -msgstr "" -"Iu registris konton kun tiu ĉi uzantonomo, sed Äi devas ankoraÅ esti " -"aktivigita." +msgstr "Iu registris konton kun tiu ĉi uzantonomo, sed Äi devas ankoraÅ esti aktivigita." #: mediagoblin/templates/mediagoblin/user_pages/user.html:79 #, python-format msgid "" "If you are that person but you've lost your verification email, you can <a " "href=\"%(login_url)s\">log in</a> and resend it." -msgstr "" -"Se vi estas tiu sed vi perdis vian kontrolmesaÄon, vi povas <a " -"href=\"%(login_url)s\">ensaluti</a> kaj resendi Äin." +msgstr "Se vi estas tiu sed vi perdis vian kontrolmesaÄon, vi povas <a href=\"%(login_url)s\">ensaluti</a> kaj resendi Äin." #: mediagoblin/templates/mediagoblin/user_pages/user.html:96 msgid "Here's a spot to tell others about yourself." @@ -614,8 +562,7 @@ msgstr "Rigardi ĉiujn dosierojn de %(username)s" msgid "" "This is where your media will appear, but you don't seem to have added " "anything yet." -msgstr "" -"Äœuste ĉi tie aperos viaj dosieroj, sed vi Åajne ankoraÅ nenion alÅutis." +msgstr "Äœuste ĉi tie aperos viaj dosieroj, sed vi Åajne ankoraÅ nenion alÅutis." #: mediagoblin/templates/mediagoblin/user_pages/user.html:163 #: mediagoblin/templates/mediagoblin/utils/object_gallery.html:72 @@ -630,9 +577,14 @@ msgstr "flusimbolo" msgid "Atom feed" msgstr "Atom-a informfluo" -#: mediagoblin/templates/mediagoblin/utils/license.html:21 -msgid "License:" -msgstr "Permesilo:" +#: mediagoblin/templates/mediagoblin/utils/geolocation_map.html:25 +msgid "Location" +msgstr "" + +#: mediagoblin/templates/mediagoblin/utils/geolocation_map.html:38 +#, python-format +msgid "View on <a href=\"%(osm_url)s\">OpenStreetMap</a>" +msgstr "" #: mediagoblin/templates/mediagoblin/utils/license.html:25 msgid "All rights reserved" @@ -650,23 +602,19 @@ msgstr "Malpli novaj →" msgid "Go to page:" msgstr "Iri al paÄo:" -#: mediagoblin/templates/mediagoblin/utils/prev_next.html:27 -#: mediagoblin/templates/mediagoblin/utils/prev_next.html:32 +#: mediagoblin/templates/mediagoblin/utils/prev_next.html:28 +#: mediagoblin/templates/mediagoblin/utils/prev_next.html:33 msgid "newer" msgstr "pli nova" -#: mediagoblin/templates/mediagoblin/utils/prev_next.html:38 -#: mediagoblin/templates/mediagoblin/utils/prev_next.html:43 +#: mediagoblin/templates/mediagoblin/utils/prev_next.html:39 +#: mediagoblin/templates/mediagoblin/utils/prev_next.html:44 msgid "older" msgstr "malpli nova" #: mediagoblin/templates/mediagoblin/utils/tags.html:20 -msgid "View more media tagged with" -msgstr "Vidi aliajn dosierojn kun la etikedo" - -#: mediagoblin/templates/mediagoblin/utils/tags.html:25 -msgid "or" -msgstr "aÅ" +msgid "Tagged with" +msgstr "" #: mediagoblin/tools/exif.py:68 msgid "Could not read the image file." @@ -676,26 +624,22 @@ msgstr "Malsukcesis lego de la bildodosiero" msgid "I am sure I want to delete this" msgstr "Mi estas certa, ke mi volas forigi ĉi tion" -#: mediagoblin/user_pages/views.py:155 +#: mediagoblin/user_pages/views.py:153 msgid "Oops, your comment was empty." msgstr "Oj, via komento estis malplena." -#: mediagoblin/user_pages/views.py:161 +#: mediagoblin/user_pages/views.py:159 msgid "Your comment has been posted!" msgstr "Via komento estis afiÅita!" -#: mediagoblin/user_pages/views.py:183 +#: mediagoblin/user_pages/views.py:185 msgid "You deleted the media." msgstr "Vi forigis la dosieron." -#: mediagoblin/user_pages/views.py:190 +#: mediagoblin/user_pages/views.py:192 msgid "The media was not deleted because you didn't check that you were sure." -msgstr "" -"La dosiero ne estis forigita, ĉar vi ne konfirmis vian certecon per la " -"markilo." +msgstr "La dosiero ne estis forigita, ĉar vi ne konfirmis vian certecon per la markilo." -#: mediagoblin/user_pages/views.py:198 +#: mediagoblin/user_pages/views.py:200 msgid "You are about to delete another user's media. Proceed with caution." msgstr "Vi estas forigonta dosieron de alia uzanto. Estu singardema." - - diff --git a/mediagoblin/i18n/es/LC_MESSAGES/mediagoblin.mo b/mediagoblin/i18n/es/LC_MESSAGES/mediagoblin.mo Binary files differindex 93a849fb..1b63a7ec 100644 --- a/mediagoblin/i18n/es/LC_MESSAGES/mediagoblin.mo +++ b/mediagoblin/i18n/es/LC_MESSAGES/mediagoblin.mo diff --git a/mediagoblin/i18n/es/LC_MESSAGES/mediagoblin.po b/mediagoblin/i18n/es/LC_MESSAGES/mediagoblin.po index 3bb46098..34524efe 100644 --- a/mediagoblin/i18n/es/LC_MESSAGES/mediagoblin.po +++ b/mediagoblin/i18n/es/LC_MESSAGES/mediagoblin.po @@ -8,17 +8,17 @@ # <jacobo@gnu.org>, 2011, 2012. # Javier Di Mauro <javierdimauro@gmail.com>, 2011. # <juangsub@gmail.com>, 2011. -# <juanma@kde.org.ar>, 2011. +# <juanma@kde.org.ar>, 2011, 2012. # Mario Rodriguez <msrodriguez00@gmail.com>, 2011. # <mu@member.fsf.org>, 2011. msgid "" msgstr "" "Project-Id-Version: GNU MediaGoblin\n" -"Report-Msgid-Bugs-To: http://bugs.foocorp.net/projects/mediagoblin/issues\n" -"POT-Creation-Date: 2012-01-29 13:31-0600\n" -"PO-Revision-Date: 2012-01-29 19:29+0000\n" +"Report-Msgid-Bugs-To: http://issues.mediagoblin.org/\n" +"POT-Creation-Date: 2012-03-18 15:16-0500\n" +"PO-Revision-Date: 2012-03-18 20:14+0000\n" "Last-Translator: cwebber <cwebber@dustycloud.org>\n" -"Language-Team: Spanish (Castilian) (http://www.transifex.net/projects/p/mediagoblin/team/es/)\n" +"Language-Team: Spanish (Castilian) (http://www.transifex.net/projects/p/mediagoblin/language/es/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" @@ -26,7 +26,7 @@ msgstr "" "Language: es\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" -#: mediagoblin/processing.py:143 +#: mediagoblin/processing.py:154 msgid "Invalid file given for media type." msgstr "Archivo inválido para el formato seleccionado." @@ -46,64 +46,52 @@ msgstr "Dirección de correo electrónico" msgid "Sorry, registration is disabled on this instance." msgstr "Lo sentimos, el registro está deshabilitado en este momento." -#: mediagoblin/auth/views.py:73 +#: mediagoblin/auth/views.py:75 msgid "Sorry, a user with that name already exists." msgstr "Lo sentimos, ya existe un usuario con ese nombre." -#: mediagoblin/auth/views.py:77 +#: mediagoblin/auth/views.py:79 msgid "Sorry, a user with that email address already exists." msgstr "Lo sentimos, ya existe un usuario con esa dirección de email." -#: mediagoblin/auth/views.py:180 +#: mediagoblin/auth/views.py:182 msgid "" "Your email address has been verified. You may now login, edit your profile, " "and submit images!" -msgstr "" -"Tu dirección de correo electrónico ha sido verificada. ¡Ahora puedes " -"ingresar, editar tu perfil, y enviar imágenes!" +msgstr "Tu dirección de correo electrónico ha sido verificada. ¡Ahora puedes ingresar, editar tu perfil, y enviar imágenes!" -#: mediagoblin/auth/views.py:186 +#: mediagoblin/auth/views.py:188 msgid "The verification key or user id is incorrect" -msgstr "" -"La clave de verificación o la identificación de usuario son incorrectas" +msgstr "La clave de verificación o la identificación de usuario son incorrectas" -#: mediagoblin/auth/views.py:204 +#: mediagoblin/auth/views.py:206 msgid "You must be logged in so we know who to send the email to!" -msgstr "" -"¡Debes iniciar sesión para que podamos saber a quién le enviamos el correo " -"electrónico!" +msgstr "¡Debes iniciar sesión para que podamos saber a quién le enviamos el correo electrónico!" -#: mediagoblin/auth/views.py:212 +#: mediagoblin/auth/views.py:214 msgid "You've already verified your email address!" msgstr "¡Ya has verificado tu dirección de correo!" -#: mediagoblin/auth/views.py:225 +#: mediagoblin/auth/views.py:227 msgid "Resent your verification email." msgstr "Se reenvió tu correo electrónico de verificación." -#: mediagoblin/auth/views.py:260 +#: mediagoblin/auth/views.py:262 msgid "" "An email has been sent with instructions on how to change your password." -msgstr "" -"Un correo electrónico ha sido enviado con instrucciones sobre cómo cambiar " -"tu contraseña." +msgstr "Un correo electrónico ha sido enviado con instrucciones sobre cómo cambiar tu contraseña." -#: mediagoblin/auth/views.py:270 +#: mediagoblin/auth/views.py:272 msgid "" "Could not send password recovery email as your username is inactive or your " "account's email address has not been verified." -msgstr "" -"No se pudo enviar un correo electrónico de recuperación de contraseñas " -"porque su nombre de usuario está inactivo o la dirección de su cuenta de " -"correo electrónico no ha sido verificada." +msgstr "No se pudo enviar un correo electrónico de recuperación de contraseñas porque su nombre de usuario está inactivo o la dirección de su cuenta de correo electrónico no ha sido verificada." -#: mediagoblin/auth/views.py:282 +#: mediagoblin/auth/views.py:284 msgid "Couldn't find someone with that username or email." -msgstr "" -"No se pudo encontrar a alguien con ese nombre de usuario o correo " -"electrónico." +msgstr "No se pudo encontrar a alguien con ese nombre de usuario o correo electrónico." -#: mediagoblin/auth/views.py:330 +#: mediagoblin/auth/views.py:332 msgid "You can now log in using your new password." msgstr "Ahora tu puedes entrar usando tu nueva contraseña." @@ -121,10 +109,7 @@ msgid "" "You can use\n" " <a href=\"http://daringfireball.net/projects/markdown/basics\">\n" " Markdown</a> for formatting." -msgstr "" -"Tu puedes usar\n" -" <a href=\"http://daringfireball.net/projects/markdown/basics\">\n" -" Markdown</a> Para el formato." +msgstr "Puedes usar\n <a href=\"http://daringfireball.net/projects/markdown/basics\">\n Markdown</a> para el formato." #: mediagoblin/edit/forms.py:33 mediagoblin/submit/forms.py:36 msgid "Tags" @@ -146,13 +131,12 @@ msgstr "La ficha no puede estar vacÃa" msgid "" "The title part of this media's address. You usually don't need to change " "this." -msgstr "" -"El tÃtulo de esta parte de la dirección de los contenidos. Por lo general no" -" es necesario cambiar esto." +msgstr "El tÃtulo de esta parte de la dirección de los contenidos. Por lo general no es necesario cambiar esto." #: mediagoblin/edit/forms.py:44 mediagoblin/submit/forms.py:41 +#: mediagoblin/templates/mediagoblin/utils/license.html:20 msgid "License" -msgstr "" +msgstr "Licencia" #: mediagoblin/edit/forms.py:50 msgid "Bio" @@ -168,34 +152,33 @@ msgstr "Vieja contraseña" #: mediagoblin/edit/forms.py:65 msgid "Enter your old password to prove you own this account." -msgstr "" -"Escriba la anterior contraseña para demostrar la propiedad de la cuenta." +msgstr "Escriba la anterior contraseña para demostrar la propiedad de la cuenta." #: mediagoblin/edit/forms.py:68 msgid "New password" msgstr "Nueva contraseña" -#: mediagoblin/edit/views.py:68 +#: mediagoblin/edit/views.py:67 msgid "An entry with that slug already exists for this user." msgstr "Una entrada con esa ficha ya existe para este usuario." -#: mediagoblin/edit/views.py:92 +#: mediagoblin/edit/views.py:88 msgid "You are editing another user's media. Proceed with caution." msgstr "Estás editando el contenido de otro usuario. Proceder con precaución." -#: mediagoblin/edit/views.py:162 +#: mediagoblin/edit/views.py:158 msgid "You are editing a user's profile. Proceed with caution." msgstr "Estás editando un perfil de usuario. Proceder con precaución." -#: mediagoblin/edit/views.py:180 +#: mediagoblin/edit/views.py:174 msgid "Profile changes saved" msgstr "Los cambios de perfil fueron salvados" -#: mediagoblin/edit/views.py:206 +#: mediagoblin/edit/views.py:200 msgid "Wrong password" msgstr "Contraseña incorrecta" -#: mediagoblin/edit/views.py:222 +#: mediagoblin/edit/views.py:216 msgid "Account settings saved" msgstr "las configuraciones de cuenta fueron salvadas" @@ -215,7 +198,7 @@ msgstr "Archivo" msgid "You must provide a file." msgstr "Debes proporcionar un archivo." -#: mediagoblin/submit/views.py:158 +#: mediagoblin/submit/views.py:156 msgid "Woohoo! Submitted!" msgstr "¡Yujú! ¡Enviado!" @@ -235,9 +218,7 @@ msgstr "Parece no haber una página en esta dirección. ¡Lo siento!" msgid "" "If you're sure the address is correct, maybe the page you're looking for has" " been moved or deleted." -msgstr "" -"Si estás seguro que la dirección es correcta, puede ser que la pagina haya " -"sido movida o borrada." +msgstr "Si estás seguro que la dirección es correcta, puede ser que la pagina haya sido movida o borrada." #: mediagoblin/templates/mediagoblin/base.html:46 msgid "MediaGoblin logo" @@ -265,10 +246,16 @@ msgstr "Conectarse" #: mediagoblin/templates/mediagoblin/base.html:84 msgid "" "Powered by <a href=\"http://mediagoblin.org\">MediaGoblin</a>, a <a " -"href=\"http://gnu.org/\">GNU</a> project" +"href=\"http://gnu.org/\">GNU</a> project." +msgstr "" + +#: mediagoblin/templates/mediagoblin/base.html:87 +#, python-format +msgid "" +"Released under the <a " +"href=\"http://www.fsf.org/licensing/licenses/agpl-3.0.html\">AGPL</a>. <a " +"href=\"%(source_link)s\">Source code</a> available." msgstr "" -"Potenciado por <a href=\"http://mediagoblin.org\">MediaGoblin</a>, a <a " -"href=\"http://gnu.org/\">GNU</a> project" #: mediagoblin/templates/mediagoblin/root.html:24 msgid "Explore" @@ -282,18 +269,13 @@ msgstr "Hola, ¡bienvenido a este sitio de MediaGoblin!" msgid "" "This site is running <a href=\"http://mediagoblin.org\">MediaGoblin</a>, an " "extraordinarily great piece of media hosting software." -msgstr "" -"Este sitio está montado con <a " -"href=\"http://mediagoblin.org\">MediaGoblin</a>, un programa libre buenÃsimo" -" para gestionar contenido multimedia." +msgstr "Este sitio está montado con <a href=\"http://mediagoblin.org\">MediaGoblin</a>, un programa libre buenÃsimo para gestionar contenido multimedia." #: mediagoblin/templates/mediagoblin/root.html:29 msgid "" "To add your own media, place comments, save your favourites and more, you " "can log in with your MediaGoblin account." -msgstr "" -"Para añadir tus propios contenidos, dejar comentarios, guardar tus favoritos" -" y más, puedes iniciar sesión con tu cuenta de MediaGoblin." +msgstr "Para añadir tus propios contenidos, dejar comentarios, guardar tus favoritos y más, puedes iniciar sesión con tu cuenta de MediaGoblin." #: mediagoblin/templates/mediagoblin/root.html:31 msgid "Don't have one yet? It's easy!" @@ -305,10 +287,7 @@ msgid "" "<a class=\"button_action_highlight\" href=\"%(register_url)s\">Create an account at this site</a>\n" " or\n" " <a class=\"button_action\" href=\"http://wiki.mediagoblin.org/HackingHowto\">Set up MediaGoblin on your own server</a>" -msgstr "" -"<a class=\"button_action_highlight\" href=\"%(register_url)s\">Crea una cuenta en este sitio</a>\n" -" o\n" -" <a class=\"button_action\" href=\"http://wiki.mediagoblin.org/HackingHowto\">Instala Mediagoblin en tu propio servidor</a>" +msgstr "<a class=\"button_action_highlight\" href=\"%(register_url)s\">Crea una cuenta en este sitio</a>\n o\n <a class=\"button_action\" href=\"http://wiki.mediagoblin.org/HackingHowto\">Instala Mediagoblin en tu propio servidor</a>" #: mediagoblin/templates/mediagoblin/root.html:40 msgid "Most recent media" @@ -342,14 +321,7 @@ msgid "" "\n" "If you think this is an error, just ignore this email and continue being\n" "a happy goblin!" -msgstr "" -"Hola %(username)s,\n" -"\n" -"Para cambiar tu contraseña de GNU MediaGoblin, abre la siguiente URL en un navegador:\n" -"\n" -"%(verification_url)s \n" -"\n" -"Si piensas que esto es un error, simplemente ignora este mensaje y sigue siendo un trasgo feliz." +msgstr "Hola %(username)s,\n\nPara cambiar tu contraseña de GNU MediaGoblin, abre la siguiente URL en un navegador:\n\n%(verification_url)s \n\nSi piensas que esto es un error, simplemente ignora este mensaje y sigue siendo un trasgo feliz." #: mediagoblin/templates/mediagoblin/auth/login.html:30 msgid "Logging in failed!" @@ -384,12 +356,7 @@ msgid "" "your web browser:\n" "\n" "%(verification_url)s" -msgstr "" -"Hola %(username)s,\n" -"\n" -"para activar tu cuenta de GNU MediaGoblin, abre la siguiente URL en tu navegador:\n" -"\n" -"%(verification_url)s " +msgstr "Hola %(username)s,\n\npara activar tu cuenta de GNU MediaGoblin, abre la siguiente URL en tu navegador:\n\n%(verification_url)s " #: mediagoblin/templates/mediagoblin/edit/edit.html:29 #, python-format @@ -424,29 +391,23 @@ msgid "Media tagged with: %(tag_name)s" msgstr "Contenido etiquetado con: %(tag_name)s" #: mediagoblin/templates/mediagoblin/media_displays/ascii.html:34 -#: mediagoblin/templates/mediagoblin/media_displays/video.html:46 +#: mediagoblin/templates/mediagoblin/media_displays/video.html:53 msgid "Original" msgstr "Original" -#: mediagoblin/templates/mediagoblin/media_displays/video.html:33 +#: mediagoblin/templates/mediagoblin/media_displays/video.html:40 msgid "" "Sorry, this video will not work because \n" "\t your web browser does not support HTML5 \n" "\t video." -msgstr "" -"Lo sentidos, este video no va funcionar porque\n" -"<span class=\"whitespace other\" title=\"Tab\">»</span> Tu explorador web no soporta HTML5\n" -"<span class=\"whitespace other\" title=\"Tab\">»</span> video." +msgstr "Lo sentidos, este video no va funcionar porque\n<span class=\"whitespace other\" title=\"Tab\">»</span> Tu explorador web no soporta HTML5\n<span class=\"whitespace other\" title=\"Tab\">»</span> video." -#: mediagoblin/templates/mediagoblin/media_displays/video.html:36 +#: mediagoblin/templates/mediagoblin/media_displays/video.html:43 msgid "" "You can get a modern web browser that \n" "\t can play this video at <a href=\"http://getfirefox.com\">\n" "\t http://getfirefox.com</a>!" -msgstr "" -"Ti puedes conseguir un navegador web moderno que\n" -"<span class=\"whitespace other\" title=\"Tab\">»</span> que puede reproducir este vÃdeo en <a href=\"http://getfirefox.com\">\n" -"<span class=\"whitespace other\" title=\"Tab\">»</span> http://getfirefox.com</a>!" +msgstr "Ti puedes conseguir un navegador web moderno que\n<span class=\"whitespace other\" title=\"Tab\">»</span> que puede reproducir este vÃdeo en <a href=\"http://getfirefox.com\">\n<span class=\"whitespace other\" title=\"Tab\">»</span> http://getfirefox.com</a>!" #: mediagoblin/templates/mediagoblin/submit/start.html:26 msgid "Add your media" @@ -466,56 +427,44 @@ msgstr "Contenidos de %(username)s" msgid "<a href=\"%(user_url)s\">%(username)s</a>'s media" msgstr "Contenido de <a href=\"%(user_url)s\">%(username)s</a>'s" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:72 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:46 #, python-format -msgid "Added on %(date)s." -msgstr "Agregado el %(date)s." +msgid "â– Browsing media by <a href=\"%(user_url)s\">%(username)s</a>" +msgstr "" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:81 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:85 msgid "Edit" msgstr "Editar" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:85 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:89 msgid "Delete" msgstr "Borrar" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:91 -#, python-format -msgid "%(comment_count)s comment" -msgstr "%(comment_count)s comentarios de" - -#: mediagoblin/templates/mediagoblin/user_pages/media.html:93 -#, python-format -msgid "%(comment_count)s comments" -msgstr "%(comment_count)s comentarios de" - -#: mediagoblin/templates/mediagoblin/user_pages/media.html:95 -msgid "No comments yet." -msgstr "No hay comentarios aún. " - -#: mediagoblin/templates/mediagoblin/user_pages/media.html:103 -msgid "Add one" -msgstr "Añadir un" +#: mediagoblin/templates/mediagoblin/user_pages/media.html:124 +msgid "Add a comment" +msgstr "" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:112 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:133 msgid "" "You can use <a " "href=\"http://daringfireball.net/projects/markdown/basics\">Markdown</a> for" " formatting." -msgstr "" +msgstr "Puedes usar <a href=\"http://daringfireball.net/projects/markdown/basics\">Markdown</a> para el formato." -#: mediagoblin/templates/mediagoblin/user_pages/media.html:116 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:137 msgid "Add this comment" msgstr "Añade un comentario " -#: mediagoblin/templates/mediagoblin/user_pages/media.html:138 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:159 msgid "at" msgstr "en" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:153 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:174 #, python-format -msgid "<p>â– Browsing media by <a href=\"%(user_url)s\">%(username)s</a></p>" -msgstr "<p>â– Buscar contenido por <a href=\"%(user_url)s\">%(username)s</a></p>" +msgid "" +"<h3>Added on</h3>\n" +" <p>%(date)s</p>" +msgstr "" #: mediagoblin/templates/mediagoblin/user_pages/media_confirm_delete.html:30 #, python-format @@ -523,8 +472,8 @@ msgid "Really delete %(title)s?" msgstr "¿Realmente deseas eliminar %(title)s?" #: mediagoblin/templates/mediagoblin/user_pages/media_confirm_delete.html:50 -msgid "Delete Permanently" -msgstr "Eliminar permanentemente" +msgid "Delete permanently" +msgstr "" #: mediagoblin/templates/mediagoblin/user_pages/processing_panel.html:22 msgid "Media processing panel" @@ -533,9 +482,7 @@ msgstr "Panel de procesamiento de contenido" #: mediagoblin/templates/mediagoblin/user_pages/processing_panel.html:25 msgid "" "You can track the state of media being processed for your gallery here." -msgstr "" -"Puedes hacer un seguimiento del estado de tu contenido siendo procesado " -"aquÃ." +msgstr "Puedes hacer un seguimiento del estado de tu contenido siendo procesado aquÃ." #: mediagoblin/templates/mediagoblin/user_pages/processing_panel.html:28 msgid "Media in-processing" @@ -571,9 +518,7 @@ msgstr "¡Casi hemos terminado! Solo falta activar la cuenta." #: mediagoblin/templates/mediagoblin/user_pages/user.html:58 msgid "" "An email should arrive in a few moments with instructions on how to do so." -msgstr "" -"En unos momentos te deberÃa llegar un correo electrónico con las " -"instrucciones para hacerlo." +msgstr "En unos momentos te deberÃa llegar un correo electrónico con las instrucciones para hacerlo." #: mediagoblin/templates/mediagoblin/user_pages/user.html:62 msgid "In case it doesn't:" @@ -587,18 +532,14 @@ msgstr "Reenviar correo electrónico de verificación" msgid "" "Someone has registered an account with this username, but it still has to be" " activated." -msgstr "" -"Alguien ya registró una cuenta con ese nombre de usuario, pero todavÃa no " -"fue activada." +msgstr "Alguien ya registró una cuenta con ese nombre de usuario, pero todavÃa no fue activada." #: mediagoblin/templates/mediagoblin/user_pages/user.html:79 #, python-format msgid "" "If you are that person but you've lost your verification email, you can <a " "href=\"%(login_url)s\">log in</a> and resend it." -msgstr "" -"Si tú eres esa persona, pero has perdido tu correo electrónico de " -"verificación, puedes <a href=\"%(login_url)s\">acceder</a> y reenviarlo." +msgstr "Si tú eres esa persona, pero has perdido tu correo electrónico de verificación, puedes <a href=\"%(login_url)s\">acceder</a> y reenviarlo." #: mediagoblin/templates/mediagoblin/user_pages/user.html:96 msgid "Here's a spot to tell others about yourself." @@ -626,8 +567,7 @@ msgstr "Ver todo el contenido de %(username)s" msgid "" "This is where your media will appear, but you don't seem to have added " "anything yet." -msgstr "" -"Aquà es donde tú contenido estará, pero parece que aún no has agregado nada." +msgstr "Aquà es donde tú contenido estará, pero parece que aún no has agregado nada." #: mediagoblin/templates/mediagoblin/user_pages/user.html:163 #: mediagoblin/templates/mediagoblin/utils/object_gallery.html:72 @@ -642,13 +582,18 @@ msgstr "Ãcono feed" msgid "Atom feed" msgstr "Atom feed" -#: mediagoblin/templates/mediagoblin/utils/license.html:21 -msgid "License:" +#: mediagoblin/templates/mediagoblin/utils/geolocation_map.html:25 +msgid "Location" +msgstr "" + +#: mediagoblin/templates/mediagoblin/utils/geolocation_map.html:38 +#, python-format +msgid "View on <a href=\"%(osm_url)s\">OpenStreetMap</a>" msgstr "" #: mediagoblin/templates/mediagoblin/utils/license.html:25 msgid "All rights reserved" -msgstr "" +msgstr "Todos los derechos reservados" #: mediagoblin/templates/mediagoblin/utils/pagination.html:39 msgid "↠Newer" @@ -662,52 +607,44 @@ msgstr "Más viejo →" msgid "Go to page:" msgstr "Ir a la página:" -#: mediagoblin/templates/mediagoblin/utils/prev_next.html:27 -#: mediagoblin/templates/mediagoblin/utils/prev_next.html:32 +#: mediagoblin/templates/mediagoblin/utils/prev_next.html:28 +#: mediagoblin/templates/mediagoblin/utils/prev_next.html:33 msgid "newer" msgstr "Más nuevo" -#: mediagoblin/templates/mediagoblin/utils/prev_next.html:38 -#: mediagoblin/templates/mediagoblin/utils/prev_next.html:43 +#: mediagoblin/templates/mediagoblin/utils/prev_next.html:39 +#: mediagoblin/templates/mediagoblin/utils/prev_next.html:44 msgid "older" msgstr "Más viejo" #: mediagoblin/templates/mediagoblin/utils/tags.html:20 -msgid "View more media tagged with" -msgstr "Ver más contenido etiquetado con" - -#: mediagoblin/templates/mediagoblin/utils/tags.html:25 -msgid "or" -msgstr "o" +msgid "Tagged with" +msgstr "" #: mediagoblin/tools/exif.py:68 msgid "Could not read the image file." -msgstr "" +msgstr "No se pudo leer el archivo de imagen." #: mediagoblin/user_pages/forms.py:30 msgid "I am sure I want to delete this" msgstr "Estoy seguro de que quiero borrar esto" -#: mediagoblin/user_pages/views.py:155 +#: mediagoblin/user_pages/views.py:153 msgid "Oops, your comment was empty." msgstr "Ups, tu comentario estaba vacÃo." -#: mediagoblin/user_pages/views.py:161 +#: mediagoblin/user_pages/views.py:159 msgid "Your comment has been posted!" msgstr "¡Tu comentario ha sido publicado!" -#: mediagoblin/user_pages/views.py:183 +#: mediagoblin/user_pages/views.py:185 msgid "You deleted the media." msgstr "Eliminaste el contenido" -#: mediagoblin/user_pages/views.py:190 +#: mediagoblin/user_pages/views.py:192 msgid "The media was not deleted because you didn't check that you were sure." msgstr "El contenido no se eliminó porque no marcaste que estabas seguro." -#: mediagoblin/user_pages/views.py:198 +#: mediagoblin/user_pages/views.py:200 msgid "You are about to delete another user's media. Proceed with caution." -msgstr "" -"Estás a punto de eliminar un contenido de otro usuario. Proceder con " -"precaución." - - +msgstr "Estás a punto de eliminar un contenido de otro usuario. Proceder con precaución." diff --git a/mediagoblin/i18n/fr/LC_MESSAGES/mediagoblin.mo b/mediagoblin/i18n/fr/LC_MESSAGES/mediagoblin.mo Binary files differindex c29f7f08..b50b30f0 100644 --- a/mediagoblin/i18n/fr/LC_MESSAGES/mediagoblin.mo +++ b/mediagoblin/i18n/fr/LC_MESSAGES/mediagoblin.mo diff --git a/mediagoblin/i18n/fr/LC_MESSAGES/mediagoblin.po b/mediagoblin/i18n/fr/LC_MESSAGES/mediagoblin.po index 3bffeef8..6171d704 100644 --- a/mediagoblin/i18n/fr/LC_MESSAGES/mediagoblin.po +++ b/mediagoblin/i18n/fr/LC_MESSAGES/mediagoblin.po @@ -13,9 +13,9 @@ msgid "" msgstr "" "Project-Id-Version: GNU MediaGoblin\n" -"Report-Msgid-Bugs-To: http://bugs.foocorp.net/projects/mediagoblin/issues\n" -"POT-Creation-Date: 2012-01-29 13:31-0600\n" -"PO-Revision-Date: 2012-01-29 19:29+0000\n" +"Report-Msgid-Bugs-To: http://issues.mediagoblin.org/\n" +"POT-Creation-Date: 2012-03-18 15:16-0500\n" +"PO-Revision-Date: 2012-03-18 20:14+0000\n" "Last-Translator: cwebber <cwebber@dustycloud.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n" "MIME-Version: 1.0\n" @@ -25,7 +25,7 @@ msgstr "" "Language: fr\n" "Plural-Forms: nplurals=2; plural=(n > 1)\n" -#: mediagoblin/processing.py:143 +#: mediagoblin/processing.py:154 msgid "Invalid file given for media type." msgstr "Le fichier envoyé ne correspond pas au type de média." @@ -45,57 +45,52 @@ msgstr "Adresse e-mail" msgid "Sorry, registration is disabled on this instance." msgstr "L'inscription n'est pas activée sur ce serveur, désolé." -#: mediagoblin/auth/views.py:73 +#: mediagoblin/auth/views.py:75 msgid "Sorry, a user with that name already exists." msgstr "Un utilisateur existe déjà avec ce nom, désolé." -#: mediagoblin/auth/views.py:77 +#: mediagoblin/auth/views.py:79 msgid "Sorry, a user with that email address already exists." msgstr "Désolé, il existe déjà un utilisateur ayant cette adresse e-mail." -#: mediagoblin/auth/views.py:180 +#: mediagoblin/auth/views.py:182 msgid "" "Your email address has been verified. You may now login, edit your profile, " "and submit images!" -msgstr "" -"Votre adresse e-mail a bien été vérifiée. Vous pouvez maintenant vous " -"identifier, modifier votre profil, et soumettre des images !" +msgstr "Votre adresse e-mail a bien été vérifiée. Vous pouvez maintenant vous identifier, modifier votre profil, et soumettre des images !" -#: mediagoblin/auth/views.py:186 +#: mediagoblin/auth/views.py:188 msgid "The verification key or user id is incorrect" msgstr "La clé de vérification ou le nom d'utilisateur est incorrect." -#: mediagoblin/auth/views.py:204 +#: mediagoblin/auth/views.py:206 msgid "You must be logged in so we know who to send the email to!" -msgstr "" -"Vous devez être authentifié afin que nous sachions à qui envoyer l'e-mail !" +msgstr "Vous devez être authentifié afin que nous sachions à qui envoyer l'e-mail !" -#: mediagoblin/auth/views.py:212 +#: mediagoblin/auth/views.py:214 msgid "You've already verified your email address!" msgstr "Votre adresse e-mail a déjà été vérifiée !" -#: mediagoblin/auth/views.py:225 +#: mediagoblin/auth/views.py:227 msgid "Resent your verification email." msgstr "E-mail de vérification renvoyé." -#: mediagoblin/auth/views.py:260 +#: mediagoblin/auth/views.py:262 msgid "" "An email has been sent with instructions on how to change your password." msgstr "" -#: mediagoblin/auth/views.py:270 +#: mediagoblin/auth/views.py:272 msgid "" "Could not send password recovery email as your username is inactive or your " "account's email address has not been verified." -msgstr "" -"Impossible d'envoyer un email de récupération de mot de passe : votre compte" -" est inactif ou bien l'email de votre compte n'a pas été vérifiée." +msgstr "Impossible d'envoyer un email de récupération de mot de passe : votre compte est inactif ou bien l'email de votre compte n'a pas été vérifiée." -#: mediagoblin/auth/views.py:282 +#: mediagoblin/auth/views.py:284 msgid "Couldn't find someone with that username or email." msgstr "" -#: mediagoblin/auth/views.py:330 +#: mediagoblin/auth/views.py:332 msgid "You can now log in using your new password." msgstr "" @@ -138,6 +133,7 @@ msgid "" msgstr "" #: mediagoblin/edit/forms.py:44 mediagoblin/submit/forms.py:41 +#: mediagoblin/templates/mediagoblin/utils/license.html:20 msgid "License" msgstr "" @@ -161,31 +157,27 @@ msgstr "" msgid "New password" msgstr "" -#: mediagoblin/edit/views.py:68 +#: mediagoblin/edit/views.py:67 msgid "An entry with that slug already exists for this user." msgstr "Une entrée existe déjà pour cet utilisateur avec la même légende." -#: mediagoblin/edit/views.py:92 +#: mediagoblin/edit/views.py:88 msgid "You are editing another user's media. Proceed with caution." -msgstr "" -"Vous vous apprêtez à modifier le média d'un autre utilisateur. Veuillez " -"prendre garde." +msgstr "Vous vous apprêtez à modifier le média d'un autre utilisateur. Veuillez prendre garde." -#: mediagoblin/edit/views.py:162 +#: mediagoblin/edit/views.py:158 msgid "You are editing a user's profile. Proceed with caution." -msgstr "" -"Vous vous apprêtez à modifier le profil d'un utilisateur. Veuillez prendre " -"garde." +msgstr "Vous vous apprêtez à modifier le profil d'un utilisateur. Veuillez prendre garde." -#: mediagoblin/edit/views.py:180 +#: mediagoblin/edit/views.py:174 msgid "Profile changes saved" msgstr "" -#: mediagoblin/edit/views.py:206 +#: mediagoblin/edit/views.py:200 msgid "Wrong password" msgstr "Mauvais mot de passe" -#: mediagoblin/edit/views.py:222 +#: mediagoblin/edit/views.py:216 msgid "Account settings saved" msgstr "" @@ -205,7 +197,7 @@ msgstr "Fichier" msgid "You must provide a file." msgstr "Il vous faut fournir un fichier." -#: mediagoblin/submit/views.py:158 +#: mediagoblin/submit/views.py:156 msgid "Woohoo! Submitted!" msgstr "Youhou, c'est envoyé !" @@ -225,9 +217,7 @@ msgstr "Il ne semble pas y avoir de page à cette adresse. Désolé !" msgid "" "If you're sure the address is correct, maybe the page you're looking for has" " been moved or deleted." -msgstr "" -"Si vous êtes sûr que l'adresse est correcte, peut-être la page que vous " -"recherchez a été déplacée ou supprimée." +msgstr "Si vous êtes sûr que l'adresse est correcte, peut-être la page que vous recherchez a été déplacée ou supprimée." #: mediagoblin/templates/mediagoblin/base.html:46 msgid "MediaGoblin logo" @@ -255,10 +245,16 @@ msgstr "S'identifier" #: mediagoblin/templates/mediagoblin/base.html:84 msgid "" "Powered by <a href=\"http://mediagoblin.org\">MediaGoblin</a>, a <a " -"href=\"http://gnu.org/\">GNU</a> project" +"href=\"http://gnu.org/\">GNU</a> project." +msgstr "" + +#: mediagoblin/templates/mediagoblin/base.html:87 +#, python-format +msgid "" +"Released under the <a " +"href=\"http://www.fsf.org/licensing/licenses/agpl-3.0.html\">AGPL</a>. <a " +"href=\"%(source_link)s\">Source code</a> available." msgstr "" -"Propulsé par <a href=\"http://mediagoblin.org\">MediaGoblin</a> , un projet " -"<a href=\"http://gnu.org/\">GNU</a>" #: mediagoblin/templates/mediagoblin/root.html:24 msgid "Explore" @@ -272,17 +268,13 @@ msgstr "Bonjour, et bienvenu sur ce site MediaGoblin !" msgid "" "This site is running <a href=\"http://mediagoblin.org\">MediaGoblin</a>, an " "extraordinarily great piece of media hosting software." -msgstr "" -"Ce site fait tourner <a href=\"http://mediagoblin.org\">MediaGoblin</a>, un " -"logiciel d'hébergement de média extraordinairement génial." +msgstr "Ce site fait tourner <a href=\"http://mediagoblin.org\">MediaGoblin</a>, un logiciel d'hébergement de média extraordinairement génial." #: mediagoblin/templates/mediagoblin/root.html:29 msgid "" "To add your own media, place comments, save your favourites and more, you " "can log in with your MediaGoblin account." -msgstr "" -"Ajoutez vos propres medias, commentez ceux des autres, sauvegardez vos " -"préférés et plus encore ! Faites tout cela depuis votre compte MediaGoblin." +msgstr "Ajoutez vos propres medias, commentez ceux des autres, sauvegardez vos préférés et plus encore ! Faites tout cela depuis votre compte MediaGoblin." #: mediagoblin/templates/mediagoblin/root.html:31 msgid "Don't have one yet? It's easy!" @@ -328,16 +320,7 @@ msgid "" "\n" "If you think this is an error, just ignore this email and continue being\n" "a happy goblin!" -msgstr "" -"Bonjour %(username)s,\n" -"\n" -"Pour changer votre mot de passe GNU MediaGoblin, ouvrez l'URL suivante dans \n" -"votre navigateur internet :\n" -"\n" -"%(verification_url)s\n" -"\n" -"Si vous pensez qu'il s'agit d'une erreur, ignorez simplement cet email et restez\n" -"un goblin heureux !" +msgstr "Bonjour %(username)s,\n\nPour changer votre mot de passe GNU MediaGoblin, ouvrez l'URL suivante dans \nvotre navigateur internet :\n\n%(verification_url)s\n\nSi vous pensez qu'il s'agit d'une erreur, ignorez simplement cet email et restez\nun goblin heureux !" #: mediagoblin/templates/mediagoblin/auth/login.html:30 msgid "Logging in failed!" @@ -372,12 +355,7 @@ msgid "" "your web browser:\n" "\n" "%(verification_url)s" -msgstr "" -"Bonjour %(username)s,\n" -"\n" -"pour activer votre compte sur GNU MediaGoblin, veuillez vous rendre à l'adresse suivante avec votre navigateur web:\n" -"\n" -"%(verification_url)s" +msgstr "Bonjour %(username)s,\n\npour activer votre compte sur GNU MediaGoblin, veuillez vous rendre à l'adresse suivante avec votre navigateur web:\n\n%(verification_url)s" #: mediagoblin/templates/mediagoblin/edit/edit.html:29 #, python-format @@ -412,18 +390,18 @@ msgid "Media tagged with: %(tag_name)s" msgstr "Médias taggés avec : %(tag_name)s " #: mediagoblin/templates/mediagoblin/media_displays/ascii.html:34 -#: mediagoblin/templates/mediagoblin/media_displays/video.html:46 +#: mediagoblin/templates/mediagoblin/media_displays/video.html:53 msgid "Original" msgstr "Original" -#: mediagoblin/templates/mediagoblin/media_displays/video.html:33 +#: mediagoblin/templates/mediagoblin/media_displays/video.html:40 msgid "" "Sorry, this video will not work because \n" "\t your web browser does not support HTML5 \n" "\t video." msgstr "" -#: mediagoblin/templates/mediagoblin/media_displays/video.html:36 +#: mediagoblin/templates/mediagoblin/media_displays/video.html:43 msgid "" "You can get a modern web browser that \n" "\t can play this video at <a href=\"http://getfirefox.com\">\n" @@ -448,55 +426,43 @@ msgstr "Medias de %(username)s" msgid "<a href=\"%(user_url)s\">%(username)s</a>'s media" msgstr "Médias de <a href=\"%(user_url)s\">%(username)s</a>" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:72 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:46 #, python-format -msgid "Added on %(date)s." +msgid "â– Browsing media by <a href=\"%(user_url)s\">%(username)s</a>" msgstr "" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:81 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:85 msgid "Edit" msgstr "Éditer" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:85 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:89 msgid "Delete" msgstr "Effacer" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:91 -#, python-format -msgid "%(comment_count)s comment" +#: mediagoblin/templates/mediagoblin/user_pages/media.html:124 +msgid "Add a comment" msgstr "" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:93 -#, python-format -msgid "%(comment_count)s comments" -msgstr "" - -#: mediagoblin/templates/mediagoblin/user_pages/media.html:95 -msgid "No comments yet." -msgstr "" - -#: mediagoblin/templates/mediagoblin/user_pages/media.html:103 -msgid "Add one" -msgstr "" - -#: mediagoblin/templates/mediagoblin/user_pages/media.html:112 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:133 msgid "" "You can use <a " "href=\"http://daringfireball.net/projects/markdown/basics\">Markdown</a> for" " formatting." msgstr "" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:116 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:137 msgid "Add this comment" msgstr "" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:138 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:159 msgid "at" msgstr "à " -#: mediagoblin/templates/mediagoblin/user_pages/media.html:153 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:174 #, python-format -msgid "<p>â– Browsing media by <a href=\"%(user_url)s\">%(username)s</a></p>" +msgid "" +"<h3>Added on</h3>\n" +" <p>%(date)s</p>" msgstr "" #: mediagoblin/templates/mediagoblin/user_pages/media_confirm_delete.html:30 @@ -505,8 +471,8 @@ msgid "Really delete %(title)s?" msgstr "Voulez-vous vraiment supprimer %(title)s ?" #: mediagoblin/templates/mediagoblin/user_pages/media_confirm_delete.html:50 -msgid "Delete Permanently" -msgstr "Supprimer définitivement" +msgid "Delete permanently" +msgstr "" #: mediagoblin/templates/mediagoblin/user_pages/processing_panel.html:22 msgid "Media processing panel" @@ -515,9 +481,7 @@ msgstr "Panneau pour le traitement des médias" #: mediagoblin/templates/mediagoblin/user_pages/processing_panel.html:25 msgid "" "You can track the state of media being processed for your gallery here." -msgstr "" -"Vous pouvez suivre l'état des médias en cours de traitement pour votre " -"galerie ici." +msgstr "Vous pouvez suivre l'état des médias en cours de traitement pour votre galerie ici." #: mediagoblin/templates/mediagoblin/user_pages/processing_panel.html:28 msgid "Media in-processing" @@ -553,9 +517,7 @@ msgstr "Presque fini ! Votre compte a encore besoin d'être activé." #: mediagoblin/templates/mediagoblin/user_pages/user.html:58 msgid "" "An email should arrive in a few moments with instructions on how to do so." -msgstr "" -"Un e-mail devrait vous parvenir dans quelques instants ; il vous indiquera " -"comment procéder." +msgstr "Un e-mail devrait vous parvenir dans quelques instants ; il vous indiquera comment procéder." #: mediagoblin/templates/mediagoblin/user_pages/user.html:62 msgid "In case it doesn't:" @@ -569,19 +531,14 @@ msgstr "Renvoyer l'e-mail de vérification" msgid "" "Someone has registered an account with this username, but it still has to be" " activated." -msgstr "" -"Quelqu'un a enregistré un compte avec ce nom, mais il doit encore être " -"activé." +msgstr "Quelqu'un a enregistré un compte avec ce nom, mais il doit encore être activé." #: mediagoblin/templates/mediagoblin/user_pages/user.html:79 #, python-format msgid "" "If you are that person but you've lost your verification email, you can <a " "href=\"%(login_url)s\">log in</a> and resend it." -msgstr "" -"Si c'est de vous qu'il s'agit, mais que vous avez perdu l'e-mail de " -"vérification, vous pouvez vous <a href=\"%(login_url)s\">identifier</a> et " -"le renvoyer." +msgstr "Si c'est de vous qu'il s'agit, mais que vous avez perdu l'e-mail de vérification, vous pouvez vous <a href=\"%(login_url)s\">identifier</a> et le renvoyer." #: mediagoblin/templates/mediagoblin/user_pages/user.html:96 msgid "Here's a spot to tell others about yourself." @@ -609,9 +566,7 @@ msgstr "Voir tous les médias de %(username)s" msgid "" "This is where your media will appear, but you don't seem to have added " "anything yet." -msgstr "" -"C'est là où vos médias apparaîssent, mais vous ne semblez pas avoir encore " -"ajouté quoi que ce soit." +msgstr "C'est là où vos médias apparaîssent, mais vous ne semblez pas avoir encore ajouté quoi que ce soit." #: mediagoblin/templates/mediagoblin/user_pages/user.html:163 #: mediagoblin/templates/mediagoblin/utils/object_gallery.html:72 @@ -626,8 +581,13 @@ msgstr "icone de flux" msgid "Atom feed" msgstr "flux Atom" -#: mediagoblin/templates/mediagoblin/utils/license.html:21 -msgid "License:" +#: mediagoblin/templates/mediagoblin/utils/geolocation_map.html:25 +msgid "Location" +msgstr "" + +#: mediagoblin/templates/mediagoblin/utils/geolocation_map.html:38 +#, python-format +msgid "View on <a href=\"%(osm_url)s\">OpenStreetMap</a>" msgstr "" #: mediagoblin/templates/mediagoblin/utils/license.html:25 @@ -646,22 +606,18 @@ msgstr "" msgid "Go to page:" msgstr "Aller à la page :" -#: mediagoblin/templates/mediagoblin/utils/prev_next.html:27 -#: mediagoblin/templates/mediagoblin/utils/prev_next.html:32 +#: mediagoblin/templates/mediagoblin/utils/prev_next.html:28 +#: mediagoblin/templates/mediagoblin/utils/prev_next.html:33 msgid "newer" msgstr "" -#: mediagoblin/templates/mediagoblin/utils/prev_next.html:38 -#: mediagoblin/templates/mediagoblin/utils/prev_next.html:43 +#: mediagoblin/templates/mediagoblin/utils/prev_next.html:39 +#: mediagoblin/templates/mediagoblin/utils/prev_next.html:44 msgid "older" msgstr "" #: mediagoblin/templates/mediagoblin/utils/tags.html:20 -msgid "View more media tagged with" -msgstr "" - -#: mediagoblin/templates/mediagoblin/utils/tags.html:25 -msgid "or" +msgid "Tagged with" msgstr "" #: mediagoblin/tools/exif.py:68 @@ -672,28 +628,22 @@ msgstr "" msgid "I am sure I want to delete this" msgstr "Je suis sûr de vouloir supprimer cela" -#: mediagoblin/user_pages/views.py:155 +#: mediagoblin/user_pages/views.py:153 msgid "Oops, your comment was empty." msgstr "Oups, votre commentaire était vide." -#: mediagoblin/user_pages/views.py:161 +#: mediagoblin/user_pages/views.py:159 msgid "Your comment has been posted!" msgstr "Votre commentaire a été posté !" -#: mediagoblin/user_pages/views.py:183 +#: mediagoblin/user_pages/views.py:185 msgid "You deleted the media." msgstr "Vous avez supprimé le media." -#: mediagoblin/user_pages/views.py:190 +#: mediagoblin/user_pages/views.py:192 msgid "The media was not deleted because you didn't check that you were sure." -msgstr "" -"Ce media n'a pas été supprimé car vous n'avez pas confirmer que vous étiez " -"sur." +msgstr "Ce media n'a pas été supprimé car vous n'avez pas confirmer que vous étiez sur." -#: mediagoblin/user_pages/views.py:198 +#: mediagoblin/user_pages/views.py:200 msgid "You are about to delete another user's media. Proceed with caution." -msgstr "" -"Vous êtes sur le point de supprimer des médias d'un autre utilisateur. " -"Procédez avec prudence." - - +msgstr "Vous êtes sur le point de supprimer des médias d'un autre utilisateur. Procédez avec prudence." diff --git a/mediagoblin/i18n/ia/LC_MESSAGES/mediagoblin.mo b/mediagoblin/i18n/ia/LC_MESSAGES/mediagoblin.mo Binary files differindex 5bf0f220..ea4d4ca2 100644 --- a/mediagoblin/i18n/ia/LC_MESSAGES/mediagoblin.mo +++ b/mediagoblin/i18n/ia/LC_MESSAGES/mediagoblin.mo diff --git a/mediagoblin/i18n/ia/LC_MESSAGES/mediagoblin.po b/mediagoblin/i18n/ia/LC_MESSAGES/mediagoblin.po index 017cd64d..c2fa2472 100644 --- a/mediagoblin/i18n/ia/LC_MESSAGES/mediagoblin.po +++ b/mediagoblin/i18n/ia/LC_MESSAGES/mediagoblin.po @@ -7,9 +7,9 @@ msgid "" msgstr "" "Project-Id-Version: GNU MediaGoblin\n" -"Report-Msgid-Bugs-To: http://bugs.foocorp.net/projects/mediagoblin/issues\n" -"POT-Creation-Date: 2012-01-29 13:31-0600\n" -"PO-Revision-Date: 2012-01-29 19:29+0000\n" +"Report-Msgid-Bugs-To: http://issues.mediagoblin.org/\n" +"POT-Creation-Date: 2012-03-18 15:16-0500\n" +"PO-Revision-Date: 2012-03-18 20:14+0000\n" "Last-Translator: cwebber <cwebber@dustycloud.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n" "MIME-Version: 1.0\n" @@ -19,7 +19,7 @@ msgstr "" "Language: ia\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" -#: mediagoblin/processing.py:143 +#: mediagoblin/processing.py:154 msgid "Invalid file given for media type." msgstr "" @@ -39,52 +39,52 @@ msgstr "Adresse de e-posta" msgid "Sorry, registration is disabled on this instance." msgstr "" -#: mediagoblin/auth/views.py:73 +#: mediagoblin/auth/views.py:75 msgid "Sorry, a user with that name already exists." msgstr "" -#: mediagoblin/auth/views.py:77 +#: mediagoblin/auth/views.py:79 msgid "Sorry, a user with that email address already exists." msgstr "" -#: mediagoblin/auth/views.py:180 +#: mediagoblin/auth/views.py:182 msgid "" "Your email address has been verified. You may now login, edit your profile, " "and submit images!" msgstr "" -#: mediagoblin/auth/views.py:186 +#: mediagoblin/auth/views.py:188 msgid "The verification key or user id is incorrect" msgstr "" -#: mediagoblin/auth/views.py:204 +#: mediagoblin/auth/views.py:206 msgid "You must be logged in so we know who to send the email to!" msgstr "" -#: mediagoblin/auth/views.py:212 +#: mediagoblin/auth/views.py:214 msgid "You've already verified your email address!" msgstr "" -#: mediagoblin/auth/views.py:225 +#: mediagoblin/auth/views.py:227 msgid "Resent your verification email." msgstr "" -#: mediagoblin/auth/views.py:260 +#: mediagoblin/auth/views.py:262 msgid "" "An email has been sent with instructions on how to change your password." msgstr "" -#: mediagoblin/auth/views.py:270 +#: mediagoblin/auth/views.py:272 msgid "" "Could not send password recovery email as your username is inactive or your " "account's email address has not been verified." msgstr "" -#: mediagoblin/auth/views.py:282 +#: mediagoblin/auth/views.py:284 msgid "Couldn't find someone with that username or email." msgstr "" -#: mediagoblin/auth/views.py:330 +#: mediagoblin/auth/views.py:332 msgid "You can now log in using your new password." msgstr "" @@ -127,6 +127,7 @@ msgid "" msgstr "" #: mediagoblin/edit/forms.py:44 mediagoblin/submit/forms.py:41 +#: mediagoblin/templates/mediagoblin/utils/license.html:20 msgid "License" msgstr "" @@ -150,27 +151,27 @@ msgstr "" msgid "New password" msgstr "" -#: mediagoblin/edit/views.py:68 +#: mediagoblin/edit/views.py:67 msgid "An entry with that slug already exists for this user." msgstr "" -#: mediagoblin/edit/views.py:92 +#: mediagoblin/edit/views.py:88 msgid "You are editing another user's media. Proceed with caution." msgstr "" -#: mediagoblin/edit/views.py:162 +#: mediagoblin/edit/views.py:158 msgid "You are editing a user's profile. Proceed with caution." msgstr "" -#: mediagoblin/edit/views.py:180 +#: mediagoblin/edit/views.py:174 msgid "Profile changes saved" msgstr "" -#: mediagoblin/edit/views.py:206 +#: mediagoblin/edit/views.py:200 msgid "Wrong password" msgstr "" -#: mediagoblin/edit/views.py:222 +#: mediagoblin/edit/views.py:216 msgid "Account settings saved" msgstr "" @@ -190,7 +191,7 @@ msgstr "" msgid "You must provide a file." msgstr "" -#: mediagoblin/submit/views.py:158 +#: mediagoblin/submit/views.py:156 msgid "Woohoo! Submitted!" msgstr "" @@ -238,7 +239,15 @@ msgstr "Initiar session" #: mediagoblin/templates/mediagoblin/base.html:84 msgid "" "Powered by <a href=\"http://mediagoblin.org\">MediaGoblin</a>, a <a " -"href=\"http://gnu.org/\">GNU</a> project" +"href=\"http://gnu.org/\">GNU</a> project." +msgstr "" + +#: mediagoblin/templates/mediagoblin/base.html:87 +#, python-format +msgid "" +"Released under the <a " +"href=\"http://www.fsf.org/licensing/licenses/agpl-3.0.html\">AGPL</a>. <a " +"href=\"%(source_link)s\">Source code</a> available." msgstr "" #: mediagoblin/templates/mediagoblin/root.html:24 @@ -375,18 +384,18 @@ msgid "Media tagged with: %(tag_name)s" msgstr "" #: mediagoblin/templates/mediagoblin/media_displays/ascii.html:34 -#: mediagoblin/templates/mediagoblin/media_displays/video.html:46 +#: mediagoblin/templates/mediagoblin/media_displays/video.html:53 msgid "Original" msgstr "" -#: mediagoblin/templates/mediagoblin/media_displays/video.html:33 +#: mediagoblin/templates/mediagoblin/media_displays/video.html:40 msgid "" "Sorry, this video will not work because \n" "\t your web browser does not support HTML5 \n" "\t video." msgstr "" -#: mediagoblin/templates/mediagoblin/media_displays/video.html:36 +#: mediagoblin/templates/mediagoblin/media_displays/video.html:43 msgid "" "You can get a modern web browser that \n" "\t can play this video at <a href=\"http://getfirefox.com\">\n" @@ -411,55 +420,43 @@ msgstr "" msgid "<a href=\"%(user_url)s\">%(username)s</a>'s media" msgstr "" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:72 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:46 #, python-format -msgid "Added on %(date)s." +msgid "â– Browsing media by <a href=\"%(user_url)s\">%(username)s</a>" msgstr "" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:81 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:85 msgid "Edit" msgstr "" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:85 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:89 msgid "Delete" msgstr "" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:91 -#, python-format -msgid "%(comment_count)s comment" -msgstr "" - -#: mediagoblin/templates/mediagoblin/user_pages/media.html:93 -#, python-format -msgid "%(comment_count)s comments" -msgstr "" - -#: mediagoblin/templates/mediagoblin/user_pages/media.html:95 -msgid "No comments yet." -msgstr "" - -#: mediagoblin/templates/mediagoblin/user_pages/media.html:103 -msgid "Add one" +#: mediagoblin/templates/mediagoblin/user_pages/media.html:124 +msgid "Add a comment" msgstr "" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:112 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:133 msgid "" "You can use <a " "href=\"http://daringfireball.net/projects/markdown/basics\">Markdown</a> for" " formatting." msgstr "" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:116 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:137 msgid "Add this comment" msgstr "" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:138 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:159 msgid "at" msgstr "" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:153 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:174 #, python-format -msgid "<p>â– Browsing media by <a href=\"%(user_url)s\">%(username)s</a></p>" +msgid "" +"<h3>Added on</h3>\n" +" <p>%(date)s</p>" msgstr "" #: mediagoblin/templates/mediagoblin/user_pages/media_confirm_delete.html:30 @@ -468,7 +465,7 @@ msgid "Really delete %(title)s?" msgstr "" #: mediagoblin/templates/mediagoblin/user_pages/media_confirm_delete.html:50 -msgid "Delete Permanently" +msgid "Delete permanently" msgstr "" #: mediagoblin/templates/mediagoblin/user_pages/processing_panel.html:22 @@ -578,8 +575,13 @@ msgstr "" msgid "Atom feed" msgstr "" -#: mediagoblin/templates/mediagoblin/utils/license.html:21 -msgid "License:" +#: mediagoblin/templates/mediagoblin/utils/geolocation_map.html:25 +msgid "Location" +msgstr "" + +#: mediagoblin/templates/mediagoblin/utils/geolocation_map.html:38 +#, python-format +msgid "View on <a href=\"%(osm_url)s\">OpenStreetMap</a>" msgstr "" #: mediagoblin/templates/mediagoblin/utils/license.html:25 @@ -598,22 +600,18 @@ msgstr "" msgid "Go to page:" msgstr "" -#: mediagoblin/templates/mediagoblin/utils/prev_next.html:27 -#: mediagoblin/templates/mediagoblin/utils/prev_next.html:32 +#: mediagoblin/templates/mediagoblin/utils/prev_next.html:28 +#: mediagoblin/templates/mediagoblin/utils/prev_next.html:33 msgid "newer" msgstr "" -#: mediagoblin/templates/mediagoblin/utils/prev_next.html:38 -#: mediagoblin/templates/mediagoblin/utils/prev_next.html:43 +#: mediagoblin/templates/mediagoblin/utils/prev_next.html:39 +#: mediagoblin/templates/mediagoblin/utils/prev_next.html:44 msgid "older" msgstr "" #: mediagoblin/templates/mediagoblin/utils/tags.html:20 -msgid "View more media tagged with" -msgstr "" - -#: mediagoblin/templates/mediagoblin/utils/tags.html:25 -msgid "or" +msgid "Tagged with" msgstr "" #: mediagoblin/tools/exif.py:68 @@ -624,24 +622,22 @@ msgstr "" msgid "I am sure I want to delete this" msgstr "" -#: mediagoblin/user_pages/views.py:155 +#: mediagoblin/user_pages/views.py:153 msgid "Oops, your comment was empty." msgstr "" -#: mediagoblin/user_pages/views.py:161 +#: mediagoblin/user_pages/views.py:159 msgid "Your comment has been posted!" msgstr "" -#: mediagoblin/user_pages/views.py:183 +#: mediagoblin/user_pages/views.py:185 msgid "You deleted the media." msgstr "" -#: mediagoblin/user_pages/views.py:190 +#: mediagoblin/user_pages/views.py:192 msgid "The media was not deleted because you didn't check that you were sure." msgstr "" -#: mediagoblin/user_pages/views.py:198 +#: mediagoblin/user_pages/views.py:200 msgid "You are about to delete another user's media. Proceed with caution." msgstr "" - - diff --git a/mediagoblin/i18n/it/LC_MESSAGES/mediagoblin.mo b/mediagoblin/i18n/it/LC_MESSAGES/mediagoblin.mo Binary files differindex 9b94ec7f..79ccdca3 100644 --- a/mediagoblin/i18n/it/LC_MESSAGES/mediagoblin.mo +++ b/mediagoblin/i18n/it/LC_MESSAGES/mediagoblin.mo diff --git a/mediagoblin/i18n/it/LC_MESSAGES/mediagoblin.po b/mediagoblin/i18n/it/LC_MESSAGES/mediagoblin.po index 2b896572..534e3f71 100644 --- a/mediagoblin/i18n/it/LC_MESSAGES/mediagoblin.po +++ b/mediagoblin/i18n/it/LC_MESSAGES/mediagoblin.po @@ -3,14 +3,15 @@ # This file is distributed under the same license as the PROJECT project. # # Translators: +# Francesco Apruzzese <cescoap@gmail.com>, 2012. # <pikappa469@alice.it>, 2011. # <robi@nunnisoft.ch>, 2011. msgid "" msgstr "" "Project-Id-Version: GNU MediaGoblin\n" -"Report-Msgid-Bugs-To: http://bugs.foocorp.net/projects/mediagoblin/issues\n" -"POT-Creation-Date: 2012-01-29 13:31-0600\n" -"PO-Revision-Date: 2012-01-29 19:29+0000\n" +"Report-Msgid-Bugs-To: http://issues.mediagoblin.org/\n" +"POT-Creation-Date: 2012-03-18 15:16-0500\n" +"PO-Revision-Date: 2012-03-18 20:14+0000\n" "Last-Translator: cwebber <cwebber@dustycloud.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n" "MIME-Version: 1.0\n" @@ -20,7 +21,7 @@ msgstr "" "Language: it\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" -#: mediagoblin/processing.py:143 +#: mediagoblin/processing.py:154 msgid "Invalid file given for media type." msgstr "documento non valido come tipo multimediale." @@ -40,59 +41,54 @@ msgstr "Indirizzo email" msgid "Sorry, registration is disabled on this instance." msgstr "Spiacente, registrazione è disabilitata su questa istanza" -#: mediagoblin/auth/views.py:73 +#: mediagoblin/auth/views.py:75 msgid "Sorry, a user with that name already exists." msgstr "Spiacente, esiste già un utente con quel nome" -#: mediagoblin/auth/views.py:77 +#: mediagoblin/auth/views.py:79 msgid "Sorry, a user with that email address already exists." msgstr "Siamo spiacenti, un utente con quell'indirizzo email esiste già ." -#: mediagoblin/auth/views.py:180 +#: mediagoblin/auth/views.py:182 msgid "" "Your email address has been verified. You may now login, edit your profile, " "and submit images!" -msgstr "" -"Il tuo indirizzo email è stato verificato. Puoi ora fare login, modificare " -"il tuo profilo, e inserire immagini!" +msgstr "Il tuo indirizzo email è stato verificato. Puoi ora fare login, modificare il tuo profilo, e inserire immagini!" -#: mediagoblin/auth/views.py:186 +#: mediagoblin/auth/views.py:188 msgid "The verification key or user id is incorrect" msgstr "La chiave di verifica o l'id utente è sbagliato" -#: mediagoblin/auth/views.py:204 +#: mediagoblin/auth/views.py:206 msgid "You must be logged in so we know who to send the email to!" -msgstr "" -"Devi entrare col tuo profilo così possiamo sapere a chi inviare l'email!" +msgstr "Devi entrare col tuo profilo così possiamo sapere a chi inviare l'email!" -#: mediagoblin/auth/views.py:212 +#: mediagoblin/auth/views.py:214 msgid "You've already verified your email address!" msgstr "Hai già verificato il tuo indirizzo email!" -#: mediagoblin/auth/views.py:225 +#: mediagoblin/auth/views.py:227 msgid "Resent your verification email." msgstr "Rispedisci email di verifica" -#: mediagoblin/auth/views.py:260 +#: mediagoblin/auth/views.py:262 msgid "" "An email has been sent with instructions on how to change your password." -msgstr "" +msgstr "Ti è stata inviata un'email con le istruzioni per cambiare la tua password." -#: mediagoblin/auth/views.py:270 +#: mediagoblin/auth/views.py:272 msgid "" "Could not send password recovery email as your username is inactive or your " "account's email address has not been verified." -msgstr "" -"Impossibile inviare l'email di recupero password perchè il tuo nome utente è" -" inattivo o il tuo account email non è stato verificato." +msgstr "Impossibile inviare l'email di recupero password perchè il tuo nome utente è inattivo o il tuo account email non è stato verificato." -#: mediagoblin/auth/views.py:282 +#: mediagoblin/auth/views.py:284 msgid "Couldn't find someone with that username or email." -msgstr "" +msgstr "Impossibile trovare qualcuno con questo username o password." -#: mediagoblin/auth/views.py:330 +#: mediagoblin/auth/views.py:332 msgid "You can now log in using your new password." -msgstr "" +msgstr "Ora puoi effettuare il login con la nuova password." #: mediagoblin/edit/forms.py:25 mediagoblin/submit/forms.py:28 msgid "Title" @@ -116,7 +112,7 @@ msgstr "Tags" #: mediagoblin/edit/forms.py:35 mediagoblin/submit/forms.py:38 msgid "Separate tags by commas." -msgstr "" +msgstr "Separa le tags con la virgola." #: mediagoblin/edit/forms.py:38 msgid "Slug" @@ -130,11 +126,12 @@ msgstr "" msgid "" "The title part of this media's address. You usually don't need to change " "this." -msgstr "" +msgstr "Il titolo è parte dell'indirizzo del contenuto. Nella maggior parte dei casi non c'è bisogno di cambiarlo." #: mediagoblin/edit/forms.py:44 mediagoblin/submit/forms.py:41 +#: mediagoblin/templates/mediagoblin/utils/license.html:20 msgid "License" -msgstr "" +msgstr "Licenza" #: mediagoblin/edit/forms.py:50 msgid "Bio" @@ -150,45 +147,43 @@ msgstr "Password vecchia" #: mediagoblin/edit/forms.py:65 msgid "Enter your old password to prove you own this account." -msgstr "" +msgstr "Inserisci la vecchia password per dimostrare di essere il proprietario dell'account." #: mediagoblin/edit/forms.py:68 msgid "New password" -msgstr "" +msgstr "Nuova password" -#: mediagoblin/edit/views.py:68 +#: mediagoblin/edit/views.py:67 msgid "An entry with that slug already exists for this user." msgstr "" -#: mediagoblin/edit/views.py:92 +#: mediagoblin/edit/views.py:88 msgid "You are editing another user's media. Proceed with caution." -msgstr "" -"Stai modificando documenti multimediale di un altro utente. Procedi con " -"attenzione." +msgstr "Stai modificando documenti multimediale di un altro utente. Procedi con attenzione." -#: mediagoblin/edit/views.py:162 +#: mediagoblin/edit/views.py:158 msgid "You are editing a user's profile. Proceed with caution." msgstr "Stai modificando il profilo di un utente. Procedi con attenzione." -#: mediagoblin/edit/views.py:180 +#: mediagoblin/edit/views.py:174 msgid "Profile changes saved" -msgstr "" +msgstr "Cambiamenti del profilo salvati" -#: mediagoblin/edit/views.py:206 +#: mediagoblin/edit/views.py:200 msgid "Wrong password" msgstr "Password errata" -#: mediagoblin/edit/views.py:222 +#: mediagoblin/edit/views.py:216 msgid "Account settings saved" -msgstr "" +msgstr "Impostazioni del profilo salvate" #: mediagoblin/media_types/__init__.py:77 msgid "Could not extract any file extension from \"{filename}\"" -msgstr "" +msgstr "Non è stato possibile estrarre un'estensione dal file da \"{filename}\"" #: mediagoblin/media_types/__init__.py:88 msgid "Sorry, I don't support that file type :(" -msgstr "" +msgstr "Mi dispiace, non supporto questo tipo di file :(" #: mediagoblin/submit/forms.py:26 msgid "File" @@ -198,7 +193,7 @@ msgstr "Documento" msgid "You must provide a file." msgstr "Devi specificare un documento." -#: mediagoblin/submit/views.py:158 +#: mediagoblin/submit/views.py:156 msgid "Woohoo! Submitted!" msgstr "Evviva! " @@ -218,9 +213,7 @@ msgstr "Non sembra esserci una pagina a questo indirizzo. Spiacente!" msgid "" "If you're sure the address is correct, maybe the page you're looking for has" " been moved or deleted." -msgstr "" -"Se sei sicuro che l'indirizzo è corretto, forse la pagina che stai cercando " -"è stata spostata o cancellata." +msgstr "Se sei sicuro che l'indirizzo è corretto, forse la pagina che stai cercando è stata spostata o cancellata." #: mediagoblin/templates/mediagoblin/base.html:46 msgid "MediaGoblin logo" @@ -248,10 +241,16 @@ msgstr "Accedi" #: mediagoblin/templates/mediagoblin/base.html:84 msgid "" "Powered by <a href=\"http://mediagoblin.org\">MediaGoblin</a>, a <a " -"href=\"http://gnu.org/\">GNU</a> project" +"href=\"http://gnu.org/\">GNU</a> project." +msgstr "" + +#: mediagoblin/templates/mediagoblin/base.html:87 +#, python-format +msgid "" +"Released under the <a " +"href=\"http://www.fsf.org/licensing/licenses/agpl-3.0.html\">AGPL</a>. <a " +"href=\"%(source_link)s\">Source code</a> available." msgstr "" -"Powered by <a href=\"http://mediagoblin.org\">MediaGoblin</a>, un progetto " -"<a href=\"http://gnu.org/\">GNU</a>" #: mediagoblin/templates/mediagoblin/root.html:24 msgid "Explore" @@ -265,18 +264,13 @@ msgstr "Ciao, benvenuto a questo sito MediaGoblin!" msgid "" "This site is running <a href=\"http://mediagoblin.org\">MediaGoblin</a>, an " "extraordinarily great piece of media hosting software." -msgstr "" -"questo sito sta utilizzando <a " -"href=\"http://mediagoblin.org\">Mediagoblin</a>, un ottimo programma di " -"media hosting." +msgstr "questo sito sta utilizzando <a href=\"http://mediagoblin.org\">Mediagoblin</a>, un ottimo programma di media hosting." #: mediagoblin/templates/mediagoblin/root.html:29 msgid "" "To add your own media, place comments, save your favourites and more, you " "can log in with your MediaGoblin account." -msgstr "" -"Per aggiungere i tuoi file, scrivere commenti, salvare i tuoi preferiti e " -"altro, devi entrare col tuo profilo MediaGoblin." +msgstr "Per aggiungere i tuoi file, scrivere commenti, salvare i tuoi preferiti e altro, devi entrare col tuo profilo MediaGoblin." #: mediagoblin/templates/mediagoblin/root.html:31 msgid "Don't have one yet? It's easy!" @@ -296,11 +290,11 @@ msgstr "Documenti multimediali più recenti" #: mediagoblin/templates/mediagoblin/auth/change_fp.html:32 msgid "Set your new password" -msgstr "" +msgstr "Imposta la nuova password" #: mediagoblin/templates/mediagoblin/auth/change_fp.html:35 msgid "Set password" -msgstr "" +msgstr "Imposta password" #: mediagoblin/templates/mediagoblin/auth/forgot_password.html:27 msgid "Recover password" @@ -322,15 +316,7 @@ msgid "" "\n" "If you think this is an error, just ignore this email and continue being\n" "a happy goblin!" -msgstr "" -"Ciao %(username)s,\n" -"per cambiare la tua password MediaGoblin apri il seguente URL\n" -"nel tuo web browser:\n" -"\n" -"%(verification_url)s\n" -"\n" -"Se pensi che sia un errore, ignora semplicemente questa email e continua ad essere \n" -"un goblin felice!" +msgstr "Ciao %(username)s,\nper cambiare la tua password MediaGoblin apri il seguente URL\nnel tuo web browser:\n\n%(verification_url)s\n\nSe pensi che sia un errore, ignora semplicemente questa email e continua ad essere \nun goblin felice!" #: mediagoblin/templates/mediagoblin/auth/login.html:30 msgid "Logging in failed!" @@ -365,12 +351,7 @@ msgid "" "your web browser:\n" "\n" "%(verification_url)s" -msgstr "" -"Ciao %(username)s,\n" -"\n" -"per attivare il tuo account GNU MediaGoblin, apri il seguente URL nel tuo navigatore web.\n" -"\n" -"%(verification_url)s" +msgstr "Ciao %(username)s,\n\nper attivare il tuo account GNU MediaGoblin, apri il seguente URL nel tuo navigatore web.\n\n%(verification_url)s" #: mediagoblin/templates/mediagoblin/edit/edit.html:29 #, python-format @@ -391,7 +372,7 @@ msgstr "Salva i cambiamenti" #: mediagoblin/templates/mediagoblin/edit/edit_account.html:34 #, python-format msgid "Changing %(username)s's account settings" -msgstr "" +msgstr "Cambio le impostazione dell'account %(username)s" #: mediagoblin/templates/mediagoblin/edit/edit_profile.html:29 #, python-format @@ -405,18 +386,18 @@ msgid "Media tagged with: %(tag_name)s" msgstr "file taggato con:%(tag_name)s" #: mediagoblin/templates/mediagoblin/media_displays/ascii.html:34 -#: mediagoblin/templates/mediagoblin/media_displays/video.html:46 +#: mediagoblin/templates/mediagoblin/media_displays/video.html:53 msgid "Original" msgstr "Originale" -#: mediagoblin/templates/mediagoblin/media_displays/video.html:33 +#: mediagoblin/templates/mediagoblin/media_displays/video.html:40 msgid "" "Sorry, this video will not work because \n" "\t your web browser does not support HTML5 \n" "\t video." msgstr "" -#: mediagoblin/templates/mediagoblin/media_displays/video.html:36 +#: mediagoblin/templates/mediagoblin/media_displays/video.html:43 msgid "" "You can get a modern web browser that \n" "\t can play this video at <a href=\"http://getfirefox.com\">\n" @@ -425,11 +406,11 @@ msgstr "" #: mediagoblin/templates/mediagoblin/submit/start.html:26 msgid "Add your media" -msgstr "" +msgstr "Aggiungi il tuo contenuto" #: mediagoblin/templates/mediagoblin/submit/start.html:30 msgid "Add" -msgstr "" +msgstr "Aggiungi" #: mediagoblin/templates/mediagoblin/user_pages/gallery.html:30 #, python-format @@ -441,55 +422,43 @@ msgstr "file di %(username)s" msgid "<a href=\"%(user_url)s\">%(username)s</a>'s media" msgstr "Documenti multimediali di <a href=\"%(user_url)s\">%(username)s</a>" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:72 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:46 #, python-format -msgid "Added on %(date)s." +msgid "â– Browsing media by <a href=\"%(user_url)s\">%(username)s</a>" msgstr "" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:81 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:85 msgid "Edit" msgstr "Modifica" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:85 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:89 msgid "Delete" msgstr "Elimina" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:91 -#, python-format -msgid "%(comment_count)s comment" -msgstr "" - -#: mediagoblin/templates/mediagoblin/user_pages/media.html:93 -#, python-format -msgid "%(comment_count)s comments" -msgstr "" - -#: mediagoblin/templates/mediagoblin/user_pages/media.html:95 -msgid "No comments yet." +#: mediagoblin/templates/mediagoblin/user_pages/media.html:124 +msgid "Add a comment" msgstr "" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:103 -msgid "Add one" -msgstr "" - -#: mediagoblin/templates/mediagoblin/user_pages/media.html:112 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:133 msgid "" "You can use <a " "href=\"http://daringfireball.net/projects/markdown/basics\">Markdown</a> for" " formatting." msgstr "" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:116 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:137 msgid "Add this comment" -msgstr "" +msgstr "Aggiungi questo commento" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:138 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:159 msgid "at" msgstr "a" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:153 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:174 #, python-format -msgid "<p>â– Browsing media by <a href=\"%(user_url)s\">%(username)s</a></p>" +msgid "" +"<h3>Added on</h3>\n" +" <p>%(date)s</p>" msgstr "" #: mediagoblin/templates/mediagoblin/user_pages/media_confirm_delete.html:30 @@ -498,8 +467,8 @@ msgid "Really delete %(title)s?" msgstr "Vuoi davvero cancellare %(title)s?" #: mediagoblin/templates/mediagoblin/user_pages/media_confirm_delete.html:50 -msgid "Delete Permanently" -msgstr "Cancella permanentemente" +msgid "Delete permanently" +msgstr "" #: mediagoblin/templates/mediagoblin/user_pages/processing_panel.html:22 msgid "Media processing panel" @@ -544,8 +513,7 @@ msgstr "Quasi finito! Il tuo account deve ancora essere attivato." #: mediagoblin/templates/mediagoblin/user_pages/user.html:58 msgid "" "An email should arrive in a few moments with instructions on how to do so." -msgstr "" -"In breve dovresti ricevere un email contenente istruzioni su come fare." +msgstr "In breve dovresti ricevere un email contenente istruzioni su come fare." #: mediagoblin/templates/mediagoblin/user_pages/user.html:62 msgid "In case it doesn't:" @@ -559,18 +527,14 @@ msgstr "Rispedisci email di verifica" msgid "" "Someone has registered an account with this username, but it still has to be" " activated." -msgstr "" -"Qualcuno ha registrato un account con questo nome utente, ma deve ancora " -"essere attivato." +msgstr "Qualcuno ha registrato un account con questo nome utente, ma deve ancora essere attivato." #: mediagoblin/templates/mediagoblin/user_pages/user.html:79 #, python-format msgid "" "If you are that person but you've lost your verification email, you can <a " "href=\"%(login_url)s\">log in</a> and resend it." -msgstr "" -"Se sei quella persona ma hai perso l'email di verifica, puoi <a " -"href=\"%(login_url)s\">accedere</a> e rispedirlo." +msgstr "Se sei quella persona ma hai perso l'email di verifica, puoi <a href=\"%(login_url)s\">accedere</a> e rispedirlo." #: mediagoblin/templates/mediagoblin/user_pages/user.html:96 msgid "Here's a spot to tell others about yourself." @@ -587,7 +551,7 @@ msgstr "Questo utente non ha (ancora) compilato il proprio profilo." #: mediagoblin/templates/mediagoblin/user_pages/user.html:125 msgid "Change account settings" -msgstr "" +msgstr "Cambia le impostazioni dell'account" #: mediagoblin/templates/mediagoblin/user_pages/user.html:138 #, python-format @@ -598,9 +562,7 @@ msgstr "Visualizza tutti i file multimediali di %(username)s" msgid "" "This is where your media will appear, but you don't seem to have added " "anything yet." -msgstr "" -"Questo è dove i tuoi documenti multimediali appariranno, ma sembra che tu " -"non abbia ancora aggiunto niente." +msgstr "Questo è dove i tuoi documenti multimediali appariranno, ma sembra che tu non abbia ancora aggiunto niente." #: mediagoblin/templates/mediagoblin/user_pages/user.html:163 #: mediagoblin/templates/mediagoblin/utils/object_gallery.html:72 @@ -615,73 +577,69 @@ msgstr "feed icon" msgid "Atom feed" msgstr "Atom feed" -#: mediagoblin/templates/mediagoblin/utils/license.html:21 -msgid "License:" +#: mediagoblin/templates/mediagoblin/utils/geolocation_map.html:25 +msgid "Location" +msgstr "" + +#: mediagoblin/templates/mediagoblin/utils/geolocation_map.html:38 +#, python-format +msgid "View on <a href=\"%(osm_url)s\">OpenStreetMap</a>" msgstr "" #: mediagoblin/templates/mediagoblin/utils/license.html:25 msgid "All rights reserved" -msgstr "" +msgstr "Tutti i diritti riservati" #: mediagoblin/templates/mediagoblin/utils/pagination.html:39 msgid "↠Newer" -msgstr "" +msgstr "↠Più recente" #: mediagoblin/templates/mediagoblin/utils/pagination.html:45 msgid "Older →" -msgstr "" +msgstr "Più vecchio →" #: mediagoblin/templates/mediagoblin/utils/pagination.html:48 msgid "Go to page:" msgstr "Vai alla pagina:" -#: mediagoblin/templates/mediagoblin/utils/prev_next.html:27 -#: mediagoblin/templates/mediagoblin/utils/prev_next.html:32 +#: mediagoblin/templates/mediagoblin/utils/prev_next.html:28 +#: mediagoblin/templates/mediagoblin/utils/prev_next.html:33 msgid "newer" -msgstr "" +msgstr "più recente" -#: mediagoblin/templates/mediagoblin/utils/prev_next.html:38 -#: mediagoblin/templates/mediagoblin/utils/prev_next.html:43 +#: mediagoblin/templates/mediagoblin/utils/prev_next.html:39 +#: mediagoblin/templates/mediagoblin/utils/prev_next.html:44 msgid "older" -msgstr "" +msgstr "più vecchio" #: mediagoblin/templates/mediagoblin/utils/tags.html:20 -msgid "View more media tagged with" -msgstr "" - -#: mediagoblin/templates/mediagoblin/utils/tags.html:25 -msgid "or" +msgid "Tagged with" msgstr "" #: mediagoblin/tools/exif.py:68 msgid "Could not read the image file." -msgstr "" +msgstr "Non è possibile leggere il file dell'immagine" #: mediagoblin/user_pages/forms.py:30 msgid "I am sure I want to delete this" msgstr "Sono sicuro di volerlo cancellare" -#: mediagoblin/user_pages/views.py:155 +#: mediagoblin/user_pages/views.py:153 msgid "Oops, your comment was empty." msgstr "Oops, il tuo commento era vuoto." -#: mediagoblin/user_pages/views.py:161 +#: mediagoblin/user_pages/views.py:159 msgid "Your comment has been posted!" msgstr "Il tuo commento è stato aggiunto!" -#: mediagoblin/user_pages/views.py:183 +#: mediagoblin/user_pages/views.py:185 msgid "You deleted the media." msgstr "Hai cancellato il file" -#: mediagoblin/user_pages/views.py:190 +#: mediagoblin/user_pages/views.py:192 msgid "The media was not deleted because you didn't check that you were sure." -msgstr "" -"Il file non è stato eliminato perchè non hai confermato di essere sicuro." +msgstr "Il file non è stato eliminato perchè non hai confermato di essere sicuro." -#: mediagoblin/user_pages/views.py:198 +#: mediagoblin/user_pages/views.py:200 msgid "You are about to delete another user's media. Proceed with caution." -msgstr "" -"Stai cancellando un documento multimediale di un altro utente. Procedi con " -"attenzione." - - +msgstr "Stai cancellando un documento multimediale di un altro utente. Procedi con attenzione." diff --git a/mediagoblin/i18n/ja/LC_MESSAGES/mediagoblin.mo b/mediagoblin/i18n/ja/LC_MESSAGES/mediagoblin.mo Binary files differindex 3b24dd2d..ad117ce9 100644 --- a/mediagoblin/i18n/ja/LC_MESSAGES/mediagoblin.mo +++ b/mediagoblin/i18n/ja/LC_MESSAGES/mediagoblin.mo diff --git a/mediagoblin/i18n/ja/LC_MESSAGES/mediagoblin.po b/mediagoblin/i18n/ja/LC_MESSAGES/mediagoblin.po index 35231f66..099ee2e0 100644 --- a/mediagoblin/i18n/ja/LC_MESSAGES/mediagoblin.po +++ b/mediagoblin/i18n/ja/LC_MESSAGES/mediagoblin.po @@ -7,9 +7,9 @@ msgid "" msgstr "" "Project-Id-Version: GNU MediaGoblin\n" -"Report-Msgid-Bugs-To: http://bugs.foocorp.net/projects/mediagoblin/issues\n" -"POT-Creation-Date: 2012-01-29 13:31-0600\n" -"PO-Revision-Date: 2012-01-29 19:29+0000\n" +"Report-Msgid-Bugs-To: http://issues.mediagoblin.org/\n" +"POT-Creation-Date: 2012-03-18 15:16-0500\n" +"PO-Revision-Date: 2012-03-18 20:14+0000\n" "Last-Translator: cwebber <cwebber@dustycloud.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n" "MIME-Version: 1.0\n" @@ -19,7 +19,7 @@ msgstr "" "Language: ja\n" "Plural-Forms: nplurals=1; plural=0\n" -#: mediagoblin/processing.py:143 +#: mediagoblin/processing.py:154 msgid "Invalid file given for media type." msgstr "" @@ -39,52 +39,52 @@ msgstr "メールアドレス" msgid "Sorry, registration is disabled on this instance." msgstr "申ã—訳ã‚りã¾ã›ã‚“ãŒã€ã“ã®ã‚¤ãƒ³ã‚¹ã‚¿ãƒ³ã‚¹ã§ç™»éŒ²ã¯ç„¡åйã«ãªã£ã¦ã„ã¾ã™ã€‚" -#: mediagoblin/auth/views.py:73 +#: mediagoblin/auth/views.py:75 msgid "Sorry, a user with that name already exists." msgstr "申ã—訳ã‚りã¾ã›ã‚“ãŒã€ãã®åå‰ã‚’æŒã¤ãƒ¦ãƒ¼ã‚¶ãƒ¼ãŒã™ã§ã«å˜åœ¨ã—ã¦ã„ã¾ã™ã€‚" -#: mediagoblin/auth/views.py:77 +#: mediagoblin/auth/views.py:79 msgid "Sorry, a user with that email address already exists." msgstr "" -#: mediagoblin/auth/views.py:180 +#: mediagoblin/auth/views.py:182 msgid "" "Your email address has been verified. You may now login, edit your profile, " "and submit images!" msgstr "メアドãŒç¢ºèªã•れã¦ã„ã¾ã™ã€‚ã“れã§ã€ãƒã‚°ã‚¤ãƒ³ã—ã¦ãƒ—ãƒãƒ•ァイルを編集ã—ã€ç”»åƒã‚’æå‡ºã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ï¼" -#: mediagoblin/auth/views.py:186 +#: mediagoblin/auth/views.py:188 msgid "The verification key or user id is incorrect" msgstr "検証ã‚ーã¾ãŸã¯ãƒ¦ãƒ¼ã‚¶ãƒ¼IDãŒé–“é•ã£ã¦ã„ã¾ã™" -#: mediagoblin/auth/views.py:204 +#: mediagoblin/auth/views.py:206 msgid "You must be logged in so we know who to send the email to!" msgstr "" -#: mediagoblin/auth/views.py:212 +#: mediagoblin/auth/views.py:214 msgid "You've already verified your email address!" msgstr "" -#: mediagoblin/auth/views.py:225 +#: mediagoblin/auth/views.py:227 msgid "Resent your verification email." msgstr "検証メールをå†é€ã—ã¾ã—ãŸã€‚" -#: mediagoblin/auth/views.py:260 +#: mediagoblin/auth/views.py:262 msgid "" "An email has been sent with instructions on how to change your password." msgstr "" -#: mediagoblin/auth/views.py:270 +#: mediagoblin/auth/views.py:272 msgid "" "Could not send password recovery email as your username is inactive or your " "account's email address has not been verified." msgstr "" -#: mediagoblin/auth/views.py:282 +#: mediagoblin/auth/views.py:284 msgid "Couldn't find someone with that username or email." msgstr "" -#: mediagoblin/auth/views.py:330 +#: mediagoblin/auth/views.py:332 msgid "You can now log in using your new password." msgstr "" @@ -127,6 +127,7 @@ msgid "" msgstr "" #: mediagoblin/edit/forms.py:44 mediagoblin/submit/forms.py:41 +#: mediagoblin/templates/mediagoblin/utils/license.html:20 msgid "License" msgstr "" @@ -150,27 +151,27 @@ msgstr "" msgid "New password" msgstr "" -#: mediagoblin/edit/views.py:68 +#: mediagoblin/edit/views.py:67 msgid "An entry with that slug already exists for this user." msgstr "ãã®ã‚¹ãƒ©ã‚°ã‚’æŒã¤ã‚¨ãƒ³ãƒˆãƒªã¯ã€ã“ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã¯æ—¢ã«å˜åœ¨ã—ã¾ã™ã€‚" -#: mediagoblin/edit/views.py:92 +#: mediagoblin/edit/views.py:88 msgid "You are editing another user's media. Proceed with caution." msgstr "ã‚ãªãŸã¯ã€ä»–ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã®ãƒ¡ãƒ‡ã‚£ã‚¢ã‚’編集ã—ã¦ã„ã¾ã™ã€‚ã”æ³¨æ„ãã ã•ã„。" -#: mediagoblin/edit/views.py:162 +#: mediagoblin/edit/views.py:158 msgid "You are editing a user's profile. Proceed with caution." msgstr "ã‚ãªãŸã¯ã€ä»–ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã®ãƒ—ãƒãƒ•ァイルを編集ã—ã¦ã„ã¾ã™ã€‚ã”æ³¨æ„ãã ã•ã„。" -#: mediagoblin/edit/views.py:180 +#: mediagoblin/edit/views.py:174 msgid "Profile changes saved" msgstr "" -#: mediagoblin/edit/views.py:206 +#: mediagoblin/edit/views.py:200 msgid "Wrong password" msgstr "" -#: mediagoblin/edit/views.py:222 +#: mediagoblin/edit/views.py:216 msgid "Account settings saved" msgstr "" @@ -190,7 +191,7 @@ msgstr "ファイル" msgid "You must provide a file." msgstr "ファイルをæä¾›ã™ã‚‹å¿…è¦ãŒã‚りã¾ã™ã€‚" -#: mediagoblin/submit/views.py:158 +#: mediagoblin/submit/views.py:156 msgid "Woohoo! Submitted!" msgstr "投稿終了ï¼" @@ -238,7 +239,15 @@ msgstr "ãƒã‚°ã‚¤ãƒ³" #: mediagoblin/templates/mediagoblin/base.html:84 msgid "" "Powered by <a href=\"http://mediagoblin.org\">MediaGoblin</a>, a <a " -"href=\"http://gnu.org/\">GNU</a> project" +"href=\"http://gnu.org/\">GNU</a> project." +msgstr "" + +#: mediagoblin/templates/mediagoblin/base.html:87 +#, python-format +msgid "" +"Released under the <a " +"href=\"http://www.fsf.org/licensing/licenses/agpl-3.0.html\">AGPL</a>. <a " +"href=\"%(source_link)s\">Source code</a> available." msgstr "" #: mediagoblin/templates/mediagoblin/root.html:24 @@ -340,12 +349,7 @@ msgid "" "your web browser:\n" "\n" "%(verification_url)s" -msgstr "" -"%(username)s様ã¸\n" -"\n" -"GNU MediaGoblinアカウントを検証ã«ã™ã‚‹ã«ã¯ã€ã“ã®URLã‚’é–‹ã„ã¦ãã ã•ã„。\n" -"\n" -"%(verification_url)s" +msgstr "%(username)s様ã¸\n\nGNU MediaGoblinアカウントを検証ã«ã™ã‚‹ã«ã¯ã€ã“ã®URLã‚’é–‹ã„ã¦ãã ã•ã„。\n\n%(verification_url)s" #: mediagoblin/templates/mediagoblin/edit/edit.html:29 #, python-format @@ -380,18 +384,18 @@ msgid "Media tagged with: %(tag_name)s" msgstr "" #: mediagoblin/templates/mediagoblin/media_displays/ascii.html:34 -#: mediagoblin/templates/mediagoblin/media_displays/video.html:46 +#: mediagoblin/templates/mediagoblin/media_displays/video.html:53 msgid "Original" msgstr "" -#: mediagoblin/templates/mediagoblin/media_displays/video.html:33 +#: mediagoblin/templates/mediagoblin/media_displays/video.html:40 msgid "" "Sorry, this video will not work because \n" "\t your web browser does not support HTML5 \n" "\t video." msgstr "" -#: mediagoblin/templates/mediagoblin/media_displays/video.html:36 +#: mediagoblin/templates/mediagoblin/media_displays/video.html:43 msgid "" "You can get a modern web browser that \n" "\t can play this video at <a href=\"http://getfirefox.com\">\n" @@ -416,55 +420,43 @@ msgstr "" msgid "<a href=\"%(user_url)s\">%(username)s</a>'s media" msgstr "<a href=\"%(user_url)s\">%(username)s</a>ã•ã‚“ã®ã‚³ãƒ³ãƒ†ãƒ³ãƒ„" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:72 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:46 #, python-format -msgid "Added on %(date)s." +msgid "â– Browsing media by <a href=\"%(user_url)s\">%(username)s</a>" msgstr "" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:81 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:85 msgid "Edit" msgstr "" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:85 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:89 msgid "Delete" msgstr "" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:91 -#, python-format -msgid "%(comment_count)s comment" -msgstr "" - -#: mediagoblin/templates/mediagoblin/user_pages/media.html:93 -#, python-format -msgid "%(comment_count)s comments" -msgstr "" - -#: mediagoblin/templates/mediagoblin/user_pages/media.html:95 -msgid "No comments yet." +#: mediagoblin/templates/mediagoblin/user_pages/media.html:124 +msgid "Add a comment" msgstr "" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:103 -msgid "Add one" -msgstr "" - -#: mediagoblin/templates/mediagoblin/user_pages/media.html:112 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:133 msgid "" "You can use <a " "href=\"http://daringfireball.net/projects/markdown/basics\">Markdown</a> for" " formatting." msgstr "" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:116 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:137 msgid "Add this comment" msgstr "" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:138 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:159 msgid "at" msgstr "" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:153 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:174 #, python-format -msgid "<p>â– Browsing media by <a href=\"%(user_url)s\">%(username)s</a></p>" +msgid "" +"<h3>Added on</h3>\n" +" <p>%(date)s</p>" msgstr "" #: mediagoblin/templates/mediagoblin/user_pages/media_confirm_delete.html:30 @@ -473,7 +465,7 @@ msgid "Really delete %(title)s?" msgstr "" #: mediagoblin/templates/mediagoblin/user_pages/media_confirm_delete.html:50 -msgid "Delete Permanently" +msgid "Delete permanently" msgstr "" #: mediagoblin/templates/mediagoblin/user_pages/processing_panel.html:22 @@ -583,8 +575,13 @@ msgstr "" msgid "Atom feed" msgstr "" -#: mediagoblin/templates/mediagoblin/utils/license.html:21 -msgid "License:" +#: mediagoblin/templates/mediagoblin/utils/geolocation_map.html:25 +msgid "Location" +msgstr "" + +#: mediagoblin/templates/mediagoblin/utils/geolocation_map.html:38 +#, python-format +msgid "View on <a href=\"%(osm_url)s\">OpenStreetMap</a>" msgstr "" #: mediagoblin/templates/mediagoblin/utils/license.html:25 @@ -603,22 +600,18 @@ msgstr "" msgid "Go to page:" msgstr "" -#: mediagoblin/templates/mediagoblin/utils/prev_next.html:27 -#: mediagoblin/templates/mediagoblin/utils/prev_next.html:32 +#: mediagoblin/templates/mediagoblin/utils/prev_next.html:28 +#: mediagoblin/templates/mediagoblin/utils/prev_next.html:33 msgid "newer" msgstr "" -#: mediagoblin/templates/mediagoblin/utils/prev_next.html:38 -#: mediagoblin/templates/mediagoblin/utils/prev_next.html:43 +#: mediagoblin/templates/mediagoblin/utils/prev_next.html:39 +#: mediagoblin/templates/mediagoblin/utils/prev_next.html:44 msgid "older" msgstr "" #: mediagoblin/templates/mediagoblin/utils/tags.html:20 -msgid "View more media tagged with" -msgstr "" - -#: mediagoblin/templates/mediagoblin/utils/tags.html:25 -msgid "or" +msgid "Tagged with" msgstr "" #: mediagoblin/tools/exif.py:68 @@ -629,24 +622,22 @@ msgstr "" msgid "I am sure I want to delete this" msgstr "" -#: mediagoblin/user_pages/views.py:155 +#: mediagoblin/user_pages/views.py:153 msgid "Oops, your comment was empty." msgstr "" -#: mediagoblin/user_pages/views.py:161 +#: mediagoblin/user_pages/views.py:159 msgid "Your comment has been posted!" msgstr "" -#: mediagoblin/user_pages/views.py:183 +#: mediagoblin/user_pages/views.py:185 msgid "You deleted the media." msgstr "" -#: mediagoblin/user_pages/views.py:190 +#: mediagoblin/user_pages/views.py:192 msgid "The media was not deleted because you didn't check that you were sure." msgstr "" -#: mediagoblin/user_pages/views.py:198 +#: mediagoblin/user_pages/views.py:200 msgid "You are about to delete another user's media. Proceed with caution." msgstr "" - - diff --git a/mediagoblin/i18n/nl/LC_MESSAGES/mediagoblin.mo b/mediagoblin/i18n/nl/LC_MESSAGES/mediagoblin.mo Binary files differindex 0baae15d..2b8f3953 100644 --- a/mediagoblin/i18n/nl/LC_MESSAGES/mediagoblin.mo +++ b/mediagoblin/i18n/nl/LC_MESSAGES/mediagoblin.mo diff --git a/mediagoblin/i18n/nl/LC_MESSAGES/mediagoblin.po b/mediagoblin/i18n/nl/LC_MESSAGES/mediagoblin.po index c40671e6..95872485 100644 --- a/mediagoblin/i18n/nl/LC_MESSAGES/mediagoblin.po +++ b/mediagoblin/i18n/nl/LC_MESSAGES/mediagoblin.po @@ -8,9 +8,9 @@ msgid "" msgstr "" "Project-Id-Version: GNU MediaGoblin\n" "Report-Msgid-Bugs-To: http://issues.mediagoblin.org/\n" -"POT-Creation-Date: 2012-01-29 13:47-0600\n" -"PO-Revision-Date: 2012-02-05 20:14+0000\n" -"Last-Translator: schendje <mail@jefvanschendel.nl>\n" +"POT-Creation-Date: 2012-03-18 15:16-0500\n" +"PO-Revision-Date: 2012-03-18 20:14+0000\n" +"Last-Translator: cwebber <cwebber@dustycloud.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -19,7 +19,7 @@ msgstr "" "Language: nl\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" -#: mediagoblin/processing.py:143 +#: mediagoblin/processing.py:154 msgid "Invalid file given for media type." msgstr "Verkeerd bestandsformaat voor mediatype opgegeven." @@ -39,59 +39,52 @@ msgstr "E-mail adres" msgid "Sorry, registration is disabled on this instance." msgstr "Sorry, registratie is uitgeschakeld op deze instantie." -#: mediagoblin/auth/views.py:73 +#: mediagoblin/auth/views.py:75 msgid "Sorry, a user with that name already exists." msgstr "Sorry, er bestaat al een gebruiker met die naam." -#: mediagoblin/auth/views.py:77 +#: mediagoblin/auth/views.py:79 msgid "Sorry, a user with that email address already exists." msgstr "Sorry, een gebruiker met dat e-mailadres bestaat al." -#: mediagoblin/auth/views.py:180 +#: mediagoblin/auth/views.py:182 msgid "" "Your email address has been verified. You may now login, edit your profile, " "and submit images!" -msgstr "" -"Uw e-mailadres is geverifieerd. U kunt nu inloggen, uw profiel bewerken, en " -"afbeeldingen toevoegen!" +msgstr "Uw e-mailadres is geverifieerd. U kunt nu inloggen, uw profiel bewerken, en afbeeldingen toevoegen!" -#: mediagoblin/auth/views.py:186 +#: mediagoblin/auth/views.py:188 msgid "The verification key or user id is incorrect" msgstr "De verificatie sleutel of gebruikers-ID is onjuist" -#: mediagoblin/auth/views.py:204 +#: mediagoblin/auth/views.py:206 msgid "You must be logged in so we know who to send the email to!" -msgstr "" -"Je moet ingelogd zijn, anders weten we niet waar we de e-mail naartoe moeten" -" sturen!" +msgstr "Je moet ingelogd zijn, anders weten we niet waar we de e-mail naartoe moeten sturen!" -#: mediagoblin/auth/views.py:212 +#: mediagoblin/auth/views.py:214 msgid "You've already verified your email address!" msgstr "Je hebt je e-mailadres al geverifieerd!" -#: mediagoblin/auth/views.py:225 +#: mediagoblin/auth/views.py:227 msgid "Resent your verification email." msgstr "Verificatie e-mail opnieuw opgestuurd." -#: mediagoblin/auth/views.py:260 +#: mediagoblin/auth/views.py:262 msgid "" "An email has been sent with instructions on how to change your password." -msgstr "" -"Een e-mail met instructies om je wachtwoord te veranderen is verstuurd." +msgstr "Een e-mail met instructies om je wachtwoord te veranderen is verstuurd." -#: mediagoblin/auth/views.py:270 +#: mediagoblin/auth/views.py:272 msgid "" "Could not send password recovery email as your username is inactive or your " "account's email address has not been verified." -msgstr "" -"Email kon niet verstuurd worden omdat je gebruikersnaam inactief is of omdat" -" je e-mailadres nog niet geverifieerd is." +msgstr "Email kon niet verstuurd worden omdat je gebruikersnaam inactief is of omdat je e-mailadres nog niet geverifieerd is." -#: mediagoblin/auth/views.py:282 +#: mediagoblin/auth/views.py:284 msgid "Couldn't find someone with that username or email." msgstr "Kon niemand vinden met die gebruikersnaam of dat e-mailadres." -#: mediagoblin/auth/views.py:330 +#: mediagoblin/auth/views.py:332 msgid "You can now log in using your new password." msgstr "Je kunt nu inloggen met je nieuwe wachtwoord." @@ -109,10 +102,7 @@ msgid "" "You can use\n" " <a href=\"http://daringfireball.net/projects/markdown/basics\">\n" " Markdown</a> for formatting." -msgstr "" -"Voor opmaak kun je <a " -"href=\"http://daringfireball.net/projects/markdown/basics\">Markdown</a> " -"gebruiken." +msgstr "Voor opmaak kun je <a href=\"http://daringfireball.net/projects/markdown/basics\">Markdown</a> gebruiken." #: mediagoblin/edit/forms.py:33 mediagoblin/submit/forms.py:36 msgid "Tags" @@ -134,11 +124,10 @@ msgstr "De slug kan niet leeg zijn" msgid "" "The title part of this media's address. You usually don't need to change " "this." -msgstr "" -"Het titelgedeelte van het adres van deze media. Normaal gesproken hoef je " -"deze niet te veranderen." +msgstr "Het titelgedeelte van het adres van deze media. Normaal gesproken hoef je deze niet te veranderen." #: mediagoblin/edit/forms.py:44 mediagoblin/submit/forms.py:41 +#: mediagoblin/templates/mediagoblin/utils/license.html:20 msgid "License" msgstr "Licentie" @@ -162,30 +151,27 @@ msgstr "Vul je oude wachtwoord in om te bewijzen dat dit jouw account is" msgid "New password" msgstr "Nieuw wachtwoord" -#: mediagoblin/edit/views.py:68 +#: mediagoblin/edit/views.py:67 msgid "An entry with that slug already exists for this user." msgstr "Er bestaat al een met die slug voor deze gebruiker." -#: mediagoblin/edit/views.py:92 +#: mediagoblin/edit/views.py:88 msgid "You are editing another user's media. Proceed with caution." -msgstr "" -"U bent de media van een andere gebruiker aan het aanpassen. Ga voorzichtig " -"te werk." +msgstr "U bent de media van een andere gebruiker aan het aanpassen. Ga voorzichtig te werk." -#: mediagoblin/edit/views.py:162 +#: mediagoblin/edit/views.py:158 msgid "You are editing a user's profile. Proceed with caution." -msgstr "" -"U bent een gebruikersprofiel aan het aanpassen. Ga voorzichtig te werk." +msgstr "U bent een gebruikersprofiel aan het aanpassen. Ga voorzichtig te werk." -#: mediagoblin/edit/views.py:180 +#: mediagoblin/edit/views.py:174 msgid "Profile changes saved" msgstr "Profielaanpassingen opgeslagen" -#: mediagoblin/edit/views.py:206 +#: mediagoblin/edit/views.py:200 msgid "Wrong password" msgstr "Verkeerd wachtwoord" -#: mediagoblin/edit/views.py:222 +#: mediagoblin/edit/views.py:216 msgid "Account settings saved" msgstr "Accountinstellingen opgeslagen" @@ -205,7 +191,7 @@ msgstr "Bestand" msgid "You must provide a file." msgstr "U moet een bestand aangeven." -#: mediagoblin/submit/views.py:158 +#: mediagoblin/submit/views.py:156 msgid "Woohoo! Submitted!" msgstr "Mooizo! Toegevoegd!" @@ -225,9 +211,7 @@ msgstr "Het lijkt erop dat er geen pagina bestaat op dit adres. Sorry!" msgid "" "If you're sure the address is correct, maybe the page you're looking for has" " been moved or deleted." -msgstr "" -"Als je zeker weet dat het adres klopt is de pagina misschien verplaatst of " -"verwijderd." +msgstr "Als je zeker weet dat het adres klopt is de pagina misschien verplaatst of verwijderd." #: mediagoblin/templates/mediagoblin/base.html:46 msgid "MediaGoblin logo" @@ -255,11 +239,16 @@ msgstr "Inloggen" #: mediagoblin/templates/mediagoblin/base.html:84 msgid "" "Powered by <a href=\"http://mediagoblin.org\">MediaGoblin</a>, a <a " -"href=\"http://gnu.org/\">GNU</a> project" +"href=\"http://gnu.org/\">GNU</a> project." +msgstr "" + +#: mediagoblin/templates/mediagoblin/base.html:87 +#, python-format +msgid "" +"Released under the <a " +"href=\"http://www.fsf.org/licensing/licenses/agpl-3.0.html\">AGPL</a>. <a " +"href=\"%(source_link)s\">Source code</a> available." msgstr "" -"Aangedreven door <a " -"href=\"http://mediagoblin.org\">MediaGoblin</a> , een <a " -"href=\"http://gnu.org/\">GNU-project</a>" #: mediagoblin/templates/mediagoblin/root.html:24 msgid "Explore" @@ -273,17 +262,13 @@ msgstr "Hoi, welkom op deze MediaGoblin website!" msgid "" "This site is running <a href=\"http://mediagoblin.org\">MediaGoblin</a>, an " "extraordinarily great piece of media hosting software." -msgstr "" -"Deze website draait <a href=\"http://mediagoblin.org\">MediaGoblin</a>, een " -"buitengewoon goed stuk software voor mediahosting." +msgstr "Deze website draait <a href=\"http://mediagoblin.org\">MediaGoblin</a>, een buitengewoon goed stuk software voor mediahosting." #: mediagoblin/templates/mediagoblin/root.html:29 msgid "" "To add your own media, place comments, save your favourites and more, you " "can log in with your MediaGoblin account." -msgstr "" -"Om je eigen media toe te voegen, berichten te plaatsen, favorieten op te " -"slaan en meer, kun je inloggen met je MediaGoblin account." +msgstr "Om je eigen media toe te voegen, berichten te plaatsen, favorieten op te slaan en meer, kun je inloggen met je MediaGoblin account." #: mediagoblin/templates/mediagoblin/root.html:31 msgid "Don't have one yet? It's easy!" @@ -295,10 +280,7 @@ msgid "" "<a class=\"button_action_highlight\" href=\"%(register_url)s\">Create an account at this site</a>\n" " or\n" " <a class=\"button_action\" href=\"http://wiki.mediagoblin.org/HackingHowto\">Set up MediaGoblin on your own server</a>" -msgstr "" -"<a class=\"button_action_highlight\" href=\"%(register_url)s\">Creëer een account op deze website</a>\n" -" of\n" -" <a class=\"button_action\" href=\"http://wiki.mediagoblin.org/HackingHowto\">Gebruik MediaGoblin op je eigen server</a>" +msgstr "<a class=\"button_action_highlight\" href=\"%(register_url)s\">Creëer een account op deze website</a>\n of\n <a class=\"button_action\" href=\"http://wiki.mediagoblin.org/HackingHowto\">Gebruik MediaGoblin op je eigen server</a>" #: mediagoblin/templates/mediagoblin/root.html:40 msgid "Most recent media" @@ -332,14 +314,7 @@ msgid "" "\n" "If you think this is an error, just ignore this email and continue being\n" "a happy goblin!" -msgstr "" -"Hoi %(username)s,\n" -"\n" -"Om je wachtwoord voor GNU MediaGoblin te veranderen, moet je dit adres in je webbrowser openen:\n" -"\n" -"%(verification_url)s\n" -"\n" -"Als je denkt dat dit niet klopt, kun je deze e-mail gewoon negeren." +msgstr "Hoi %(username)s,\n\nOm je wachtwoord voor GNU MediaGoblin te veranderen, moet je dit adres in je webbrowser openen:\n\n%(verification_url)s\n\nAls je denkt dat dit niet klopt, kun je deze e-mail gewoon negeren." #: mediagoblin/templates/mediagoblin/auth/login.html:30 msgid "Logging in failed!" @@ -374,9 +349,7 @@ msgid "" "your web browser:\n" "\n" "%(verification_url)s" -msgstr "" -"Hallo %(username)s , open de volgende URL in uw webbrowser om uw GNU " -"MediaGoblin account te activeren: %(verification_url)s " +msgstr "Hallo %(username)s , open de volgende URL in uw webbrowser om uw GNU MediaGoblin account te activeren: %(verification_url)s " #: mediagoblin/templates/mediagoblin/edit/edit.html:29 #, python-format @@ -411,27 +384,23 @@ msgid "Media tagged with: %(tag_name)s" msgstr "Media met het label: %(tag_name)s" #: mediagoblin/templates/mediagoblin/media_displays/ascii.html:34 -#: mediagoblin/templates/mediagoblin/media_displays/video.html:46 +#: mediagoblin/templates/mediagoblin/media_displays/video.html:53 msgid "Original" msgstr "Origineel" -#: mediagoblin/templates/mediagoblin/media_displays/video.html:33 +#: mediagoblin/templates/mediagoblin/media_displays/video.html:40 msgid "" "Sorry, this video will not work because \n" "\t your web browser does not support HTML5 \n" "\t video." -msgstr "" -"Sorry, deze video werkt niet omdat je webbrowser geen HTML5 video " -"ondersteunt." +msgstr "Sorry, deze video werkt niet omdat je webbrowser geen HTML5 video ondersteunt." -#: mediagoblin/templates/mediagoblin/media_displays/video.html:36 +#: mediagoblin/templates/mediagoblin/media_displays/video.html:43 msgid "" "You can get a modern web browser that \n" "\t can play this video at <a href=\"http://getfirefox.com\">\n" "\t http://getfirefox.com</a>!" -msgstr "" -"Je kunt een moderne webbrowser die deze video af kan spelen krijgen op <a " -"href=\"http://getfirefox.com\">http://getfirefox.com</a>!" +msgstr "Je kunt een moderne webbrowser die deze video af kan spelen krijgen op <a href=\"http://getfirefox.com\">http://getfirefox.com</a>!" #: mediagoblin/templates/mediagoblin/submit/start.html:26 msgid "Add your media" @@ -451,61 +420,44 @@ msgstr "Media van %(username)s" msgid "<a href=\"%(user_url)s\">%(username)s</a>'s media" msgstr "Media van <a href=\"%(user_url)s\"> %(username)s </a>" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:72 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:46 #, python-format -msgid "Added on %(date)s." -msgstr "Toegevoegd op %(date)s." +msgid "â– Browsing media by <a href=\"%(user_url)s\">%(username)s</a>" +msgstr "" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:81 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:85 msgid "Edit" msgstr "Pas aan" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:85 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:89 msgid "Delete" msgstr "Verwijderen" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:91 -#, python-format -msgid "%(comment_count)s comment" -msgstr "%(comment_count)s bericht" - -#: mediagoblin/templates/mediagoblin/user_pages/media.html:93 -#, python-format -msgid "%(comment_count)s comments" -msgstr "%(comment_count)s berichten" - -#: mediagoblin/templates/mediagoblin/user_pages/media.html:95 -msgid "No comments yet." -msgstr "Er zijn nog geen berichten." - -#: mediagoblin/templates/mediagoblin/user_pages/media.html:103 -msgid "Add one" -msgstr "Voeg er een toe" +#: mediagoblin/templates/mediagoblin/user_pages/media.html:124 +msgid "Add a comment" +msgstr "" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:112 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:133 msgid "" "You can use <a " "href=\"http://daringfireball.net/projects/markdown/basics\">Markdown</a> for" " formatting." -msgstr "" -"Voor opmaak kun je <a " -"href=\"http://daringfireball.net/projects/markdown/basics\">Markdown</a>" -" gebruiken." +msgstr "Voor opmaak kun je <a href=\"http://daringfireball.net/projects/markdown/basics\">Markdown</a> gebruiken." -#: mediagoblin/templates/mediagoblin/user_pages/media.html:116 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:137 msgid "Add this comment" msgstr "Voeg dit bericht toe" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:138 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:159 msgid "at" msgstr "op" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:153 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:174 #, python-format -msgid "<p>â– Browsing media by <a href=\"%(user_url)s\">%(username)s</a></p>" +msgid "" +"<h3>Added on</h3>\n" +" <p>%(date)s</p>" msgstr "" -"<p>â– Media aan het bekijken van <a " -"href=\"%(user_url)s\">%(username)s</a></p>" #: mediagoblin/templates/mediagoblin/user_pages/media_confirm_delete.html:30 #, python-format @@ -513,8 +465,8 @@ msgid "Really delete %(title)s?" msgstr "Zeker weten dat je %(title)s wil verwijderen?" #: mediagoblin/templates/mediagoblin/user_pages/media_confirm_delete.html:50 -msgid "Delete Permanently" -msgstr "Permanent verwijderen" +msgid "Delete permanently" +msgstr "" #: mediagoblin/templates/mediagoblin/user_pages/processing_panel.html:22 msgid "Media processing panel" @@ -559,9 +511,7 @@ msgstr "Bijna klaar! Je account moet nog geactiveerd worden." #: mediagoblin/templates/mediagoblin/user_pages/user.html:58 msgid "" "An email should arrive in a few moments with instructions on how to do so." -msgstr "" -"Een e-mail zou in een paar ogenblikken aan moeten komen met instructies " -"hiertoe." +msgstr "Een e-mail zou in een paar ogenblikken aan moeten komen met instructies hiertoe." #: mediagoblin/templates/mediagoblin/user_pages/user.html:62 msgid "In case it doesn't:" @@ -575,18 +525,14 @@ msgstr "Stuur de verificatie e-mail opnieuw op." msgid "" "Someone has registered an account with this username, but it still has to be" " activated." -msgstr "" -"Iemand heeft een account met deze gebruikersnaam gemaakt, maar hij moet nog " -"geactiveerd worden." +msgstr "Iemand heeft een account met deze gebruikersnaam gemaakt, maar hij moet nog geactiveerd worden." #: mediagoblin/templates/mediagoblin/user_pages/user.html:79 #, python-format msgid "" "If you are that person but you've lost your verification email, you can <a " "href=\"%(login_url)s\">log in</a> and resend it." -msgstr "" -"Als u die persoon bent, maar de verificatie e-mail verloren hebt, kunt u <a " -"href=\"%(login_url)s\">inloggen</a> en hem nogmaals verzenden." +msgstr "Als u die persoon bent, maar de verificatie e-mail verloren hebt, kunt u <a href=\"%(login_url)s\">inloggen</a> en hem nogmaals verzenden." #: mediagoblin/templates/mediagoblin/user_pages/user.html:96 msgid "Here's a spot to tell others about yourself." @@ -614,9 +560,7 @@ msgstr "Bekijk alle media van %(username)s" msgid "" "This is where your media will appear, but you don't seem to have added " "anything yet." -msgstr "" -"Dit is waar je nieuwe media zal verschijnen, maar het lijkt erop dat je nog " -"niets heb toegevoegd." +msgstr "Dit is waar je nieuwe media zal verschijnen, maar het lijkt erop dat je nog niets heb toegevoegd." #: mediagoblin/templates/mediagoblin/user_pages/user.html:163 #: mediagoblin/templates/mediagoblin/utils/object_gallery.html:72 @@ -631,9 +575,14 @@ msgstr "feed icoon" msgid "Atom feed" msgstr "Atom feed" -#: mediagoblin/templates/mediagoblin/utils/license.html:21 -msgid "License:" -msgstr "Licentie:" +#: mediagoblin/templates/mediagoblin/utils/geolocation_map.html:25 +msgid "Location" +msgstr "" + +#: mediagoblin/templates/mediagoblin/utils/geolocation_map.html:38 +#, python-format +msgid "View on <a href=\"%(osm_url)s\">OpenStreetMap</a>" +msgstr "" #: mediagoblin/templates/mediagoblin/utils/license.html:25 msgid "All rights reserved" @@ -651,23 +600,19 @@ msgstr "Ouder →" msgid "Go to page:" msgstr "Ga naar pagina:" -#: mediagoblin/templates/mediagoblin/utils/prev_next.html:27 -#: mediagoblin/templates/mediagoblin/utils/prev_next.html:32 +#: mediagoblin/templates/mediagoblin/utils/prev_next.html:28 +#: mediagoblin/templates/mediagoblin/utils/prev_next.html:33 msgid "newer" msgstr "nieuwer" -#: mediagoblin/templates/mediagoblin/utils/prev_next.html:38 -#: mediagoblin/templates/mediagoblin/utils/prev_next.html:43 +#: mediagoblin/templates/mediagoblin/utils/prev_next.html:39 +#: mediagoblin/templates/mediagoblin/utils/prev_next.html:44 msgid "older" msgstr "ouder" #: mediagoblin/templates/mediagoblin/utils/tags.html:20 -msgid "View more media tagged with" -msgstr "Bekijk meer media gelabeld met" - -#: mediagoblin/templates/mediagoblin/utils/tags.html:25 -msgid "or" -msgstr "of" +msgid "Tagged with" +msgstr "" #: mediagoblin/tools/exif.py:68 msgid "Could not read the image file." @@ -677,27 +622,22 @@ msgstr "Kon het afbeeldingsbestand niet lezen." msgid "I am sure I want to delete this" msgstr "Ik weet zeker dat ik dit wil verwijderen." -#: mediagoblin/user_pages/views.py:155 +#: mediagoblin/user_pages/views.py:153 msgid "Oops, your comment was empty." msgstr "Oeps, je bericht was leeg." -#: mediagoblin/user_pages/views.py:161 +#: mediagoblin/user_pages/views.py:159 msgid "Your comment has been posted!" msgstr "Je bericht is geplaatst!" -#: mediagoblin/user_pages/views.py:183 +#: mediagoblin/user_pages/views.py:185 msgid "You deleted the media." msgstr "Je hebt deze media verwijderd." -#: mediagoblin/user_pages/views.py:190 +#: mediagoblin/user_pages/views.py:192 msgid "The media was not deleted because you didn't check that you were sure." -msgstr "" -"Deze media was niet verwijderd omdat je niet hebt aangegeven dat je het " -"zeker weet." +msgstr "Deze media was niet verwijderd omdat je niet hebt aangegeven dat je het zeker weet." -#: mediagoblin/user_pages/views.py:198 +#: mediagoblin/user_pages/views.py:200 msgid "You are about to delete another user's media. Proceed with caution." -msgstr "" -"Je staat op het punt de media van iemand anders te verwijderen. Pas op." - - +msgstr "Je staat op het punt de media van iemand anders te verwijderen. Pas op." diff --git a/mediagoblin/i18n/nn_NO/LC_MESSAGES/mediagoblin.mo b/mediagoblin/i18n/nn_NO/LC_MESSAGES/mediagoblin.mo Binary files differindex 01179e47..2d085d29 100644 --- a/mediagoblin/i18n/nn_NO/LC_MESSAGES/mediagoblin.mo +++ b/mediagoblin/i18n/nn_NO/LC_MESSAGES/mediagoblin.mo diff --git a/mediagoblin/i18n/nn_NO/LC_MESSAGES/mediagoblin.po b/mediagoblin/i18n/nn_NO/LC_MESSAGES/mediagoblin.po index 1cc0b878..9ce8914b 100644 --- a/mediagoblin/i18n/nn_NO/LC_MESSAGES/mediagoblin.po +++ b/mediagoblin/i18n/nn_NO/LC_MESSAGES/mediagoblin.po @@ -7,9 +7,9 @@ msgid "" msgstr "" "Project-Id-Version: GNU MediaGoblin\n" -"Report-Msgid-Bugs-To: http://bugs.foocorp.net/projects/mediagoblin/issues\n" -"POT-Creation-Date: 2012-01-29 13:31-0600\n" -"PO-Revision-Date: 2012-01-29 19:29+0000\n" +"Report-Msgid-Bugs-To: http://issues.mediagoblin.org/\n" +"POT-Creation-Date: 2012-03-18 15:16-0500\n" +"PO-Revision-Date: 2012-03-18 20:14+0000\n" "Last-Translator: cwebber <cwebber@dustycloud.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n" "MIME-Version: 1.0\n" @@ -19,7 +19,7 @@ msgstr "" "Language: nn_NO\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" -#: mediagoblin/processing.py:143 +#: mediagoblin/processing.py:154 msgid "Invalid file given for media type." msgstr "Ugyldig fil for mediatypen." @@ -39,55 +39,52 @@ msgstr "Epost" msgid "Sorry, registration is disabled on this instance." msgstr "Registrering er slege av. Orsak." -#: mediagoblin/auth/views.py:73 +#: mediagoblin/auth/views.py:75 msgid "Sorry, a user with that name already exists." msgstr "Ein konto med dette brukarnamnet finst allereide." -#: mediagoblin/auth/views.py:77 +#: mediagoblin/auth/views.py:79 msgid "Sorry, a user with that email address already exists." msgstr "" -#: mediagoblin/auth/views.py:180 +#: mediagoblin/auth/views.py:182 msgid "" "Your email address has been verified. You may now login, edit your profile, " "and submit images!" -msgstr "" -"Kontoen din er stadfesta. Du kan no logga inn, endra profilen din og lasta " -"opp filer." +msgstr "Kontoen din er stadfesta. Du kan no logga inn, endra profilen din og lasta opp filer." -#: mediagoblin/auth/views.py:186 +#: mediagoblin/auth/views.py:188 msgid "The verification key or user id is incorrect" msgstr "Stadfestingsnykelen eller brukar-ID-en din er feil." -#: mediagoblin/auth/views.py:204 +#: mediagoblin/auth/views.py:206 msgid "You must be logged in so we know who to send the email to!" msgstr "" -#: mediagoblin/auth/views.py:212 +#: mediagoblin/auth/views.py:214 msgid "You've already verified your email address!" msgstr "" -#: mediagoblin/auth/views.py:225 +#: mediagoblin/auth/views.py:227 msgid "Resent your verification email." msgstr "Send ein ny stadfestingsepost." -#: mediagoblin/auth/views.py:260 +#: mediagoblin/auth/views.py:262 msgid "" "An email has been sent with instructions on how to change your password." msgstr "" -#: mediagoblin/auth/views.py:270 +#: mediagoblin/auth/views.py:272 msgid "" "Could not send password recovery email as your username is inactive or your " "account's email address has not been verified." -msgstr "" -"Kunne ikkje senda epost. Brukarnamnet ditt er inaktivt eller uverifisert." +msgstr "Kunne ikkje senda epost. Brukarnamnet ditt er inaktivt eller uverifisert." -#: mediagoblin/auth/views.py:282 +#: mediagoblin/auth/views.py:284 msgid "Couldn't find someone with that username or email." msgstr "" -#: mediagoblin/auth/views.py:330 +#: mediagoblin/auth/views.py:332 msgid "You can now log in using your new password." msgstr "" @@ -130,6 +127,7 @@ msgid "" msgstr "" #: mediagoblin/edit/forms.py:44 mediagoblin/submit/forms.py:41 +#: mediagoblin/templates/mediagoblin/utils/license.html:20 msgid "License" msgstr "" @@ -153,27 +151,27 @@ msgstr "" msgid "New password" msgstr "" -#: mediagoblin/edit/views.py:68 +#: mediagoblin/edit/views.py:67 msgid "An entry with that slug already exists for this user." msgstr "Eit innlegg med denne adressetittelen finst allereie." -#: mediagoblin/edit/views.py:92 +#: mediagoblin/edit/views.py:88 msgid "You are editing another user's media. Proceed with caution." msgstr "TrÃ¥ varsamt, du endrar nokon andre sine mediefiler." -#: mediagoblin/edit/views.py:162 +#: mediagoblin/edit/views.py:158 msgid "You are editing a user's profile. Proceed with caution." msgstr "TrÃ¥ varsamt, du endrar nokon andre sin profil." -#: mediagoblin/edit/views.py:180 +#: mediagoblin/edit/views.py:174 msgid "Profile changes saved" msgstr "" -#: mediagoblin/edit/views.py:206 +#: mediagoblin/edit/views.py:200 msgid "Wrong password" msgstr "" -#: mediagoblin/edit/views.py:222 +#: mediagoblin/edit/views.py:216 msgid "Account settings saved" msgstr "" @@ -193,7 +191,7 @@ msgstr "Fil" msgid "You must provide a file." msgstr "Du mÃ¥ velja ei fil." -#: mediagoblin/submit/views.py:158 +#: mediagoblin/submit/views.py:156 msgid "Woohoo! Submitted!" msgstr "Johoo! Opplasta!" @@ -213,9 +211,7 @@ msgstr "Det ser ikkje ut til Ã¥ vera noko her... Orsak." msgid "" "If you're sure the address is correct, maybe the page you're looking for has" " been moved or deleted." -msgstr "" -"Er du sikker pÃ¥ at adressa er korrekt, so er sida truleg flytta eller " -"sletta." +msgstr "Er du sikker pÃ¥ at adressa er korrekt, so er sida truleg flytta eller sletta." #: mediagoblin/templates/mediagoblin/base.html:46 msgid "MediaGoblin logo" @@ -243,10 +239,16 @@ msgstr "Logg inn" #: mediagoblin/templates/mediagoblin/base.html:84 msgid "" "Powered by <a href=\"http://mediagoblin.org\">MediaGoblin</a>, a <a " -"href=\"http://gnu.org/\">GNU</a> project" +"href=\"http://gnu.org/\">GNU</a> project." +msgstr "" + +#: mediagoblin/templates/mediagoblin/base.html:87 +#, python-format +msgid "" +"Released under the <a " +"href=\"http://www.fsf.org/licensing/licenses/agpl-3.0.html\">AGPL</a>. <a " +"href=\"%(source_link)s\">Source code</a> available." msgstr "" -"Drive av <a href=\"http://mediagoblin.org\">MediaGoblin</a>, eit <a " -"href=\"http://gnu.org/\">GNU</a>-prosjekt" #: mediagoblin/templates/mediagoblin/root.html:24 msgid "Explore" @@ -312,14 +314,7 @@ msgid "" "\n" "If you think this is an error, just ignore this email and continue being\n" "a happy goblin!" -msgstr "" -"Hei %(username)s,\n" -"\n" -"for Ã¥ endra MediaGoblin-passordet ditt, opna fylgjande URL i ein netlesar:\n" -"\n" -" <%(verification_url)s>\n" -"\n" -"Dersom du mistenkjer dette er eit misstak, ignorer eposten og hald fram med Ã¥ vera ein glad goblin!" +msgstr "Hei %(username)s,\n\nfor Ã¥ endra MediaGoblin-passordet ditt, opna fylgjande URL i ein netlesar:\n\n <%(verification_url)s>\n\nDersom du mistenkjer dette er eit misstak, ignorer eposten og hald fram med Ã¥ vera ein glad goblin!" #: mediagoblin/templates/mediagoblin/auth/login.html:30 msgid "Logging in failed!" @@ -354,12 +349,7 @@ msgid "" "your web browser:\n" "\n" "%(verification_url)s" -msgstr "" -"Hei %(username)s,\n" -"\n" -"opna fylgjande netadresse i netlesaren din for Ã¥ aktivera kontoen din:\n" -"\n" -"%(verification_url)s" +msgstr "Hei %(username)s,\n\nopna fylgjande netadresse i netlesaren din for Ã¥ aktivera kontoen din:\n\n%(verification_url)s" #: mediagoblin/templates/mediagoblin/edit/edit.html:29 #, python-format @@ -394,18 +384,18 @@ msgid "Media tagged with: %(tag_name)s" msgstr "" #: mediagoblin/templates/mediagoblin/media_displays/ascii.html:34 -#: mediagoblin/templates/mediagoblin/media_displays/video.html:46 +#: mediagoblin/templates/mediagoblin/media_displays/video.html:53 msgid "Original" msgstr "" -#: mediagoblin/templates/mediagoblin/media_displays/video.html:33 +#: mediagoblin/templates/mediagoblin/media_displays/video.html:40 msgid "" "Sorry, this video will not work because \n" "\t your web browser does not support HTML5 \n" "\t video." msgstr "" -#: mediagoblin/templates/mediagoblin/media_displays/video.html:36 +#: mediagoblin/templates/mediagoblin/media_displays/video.html:43 msgid "" "You can get a modern web browser that \n" "\t can play this video at <a href=\"http://getfirefox.com\">\n" @@ -430,55 +420,43 @@ msgstr "" msgid "<a href=\"%(user_url)s\">%(username)s</a>'s media" msgstr "<a href=\"%(user_url)s\">%(username)s</a> sine mediefiler" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:72 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:46 #, python-format -msgid "Added on %(date)s." +msgid "â– Browsing media by <a href=\"%(user_url)s\">%(username)s</a>" msgstr "" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:81 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:85 msgid "Edit" msgstr "" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:85 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:89 msgid "Delete" msgstr "" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:91 -#, python-format -msgid "%(comment_count)s comment" -msgstr "" - -#: mediagoblin/templates/mediagoblin/user_pages/media.html:93 -#, python-format -msgid "%(comment_count)s comments" -msgstr "" - -#: mediagoblin/templates/mediagoblin/user_pages/media.html:95 -msgid "No comments yet." +#: mediagoblin/templates/mediagoblin/user_pages/media.html:124 +msgid "Add a comment" msgstr "" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:103 -msgid "Add one" -msgstr "" - -#: mediagoblin/templates/mediagoblin/user_pages/media.html:112 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:133 msgid "" "You can use <a " "href=\"http://daringfireball.net/projects/markdown/basics\">Markdown</a> for" " formatting." msgstr "" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:116 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:137 msgid "Add this comment" msgstr "" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:138 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:159 msgid "at" msgstr "" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:153 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:174 #, python-format -msgid "<p>â– Browsing media by <a href=\"%(user_url)s\">%(username)s</a></p>" +msgid "" +"<h3>Added on</h3>\n" +" <p>%(date)s</p>" msgstr "" #: mediagoblin/templates/mediagoblin/user_pages/media_confirm_delete.html:30 @@ -487,8 +465,8 @@ msgid "Really delete %(title)s?" msgstr "Vil du verkeleg sletta %(title)s?" #: mediagoblin/templates/mediagoblin/user_pages/media_confirm_delete.html:50 -msgid "Delete Permanently" -msgstr "Slett permament" +msgid "Delete permanently" +msgstr "" #: mediagoblin/templates/mediagoblin/user_pages/processing_panel.html:22 msgid "Media processing panel" @@ -554,9 +532,7 @@ msgstr "Dette brukarnamnet finst allereie, men det er ikkje aktivert." msgid "" "If you are that person but you've lost your verification email, you can <a " "href=\"%(login_url)s\">log in</a> and resend it." -msgstr "" -"Viss dette er deg, kan du <a href=\"%(login_url)s\">logga inn</a> for Ã¥ fÃ¥ " -"tilsendt ny epost med stadfestingslenkje." +msgstr "Viss dette er deg, kan du <a href=\"%(login_url)s\">logga inn</a> for Ã¥ fÃ¥ tilsendt ny epost med stadfestingslenkje." #: mediagoblin/templates/mediagoblin/user_pages/user.html:96 msgid "Here's a spot to tell others about yourself." @@ -599,8 +575,13 @@ msgstr " " msgid "Atom feed" msgstr "Atom-kjelde" -#: mediagoblin/templates/mediagoblin/utils/license.html:21 -msgid "License:" +#: mediagoblin/templates/mediagoblin/utils/geolocation_map.html:25 +msgid "Location" +msgstr "" + +#: mediagoblin/templates/mediagoblin/utils/geolocation_map.html:38 +#, python-format +msgid "View on <a href=\"%(osm_url)s\">OpenStreetMap</a>" msgstr "" #: mediagoblin/templates/mediagoblin/utils/license.html:25 @@ -619,22 +600,18 @@ msgstr "" msgid "Go to page:" msgstr "" -#: mediagoblin/templates/mediagoblin/utils/prev_next.html:27 -#: mediagoblin/templates/mediagoblin/utils/prev_next.html:32 +#: mediagoblin/templates/mediagoblin/utils/prev_next.html:28 +#: mediagoblin/templates/mediagoblin/utils/prev_next.html:33 msgid "newer" msgstr "" -#: mediagoblin/templates/mediagoblin/utils/prev_next.html:38 -#: mediagoblin/templates/mediagoblin/utils/prev_next.html:43 +#: mediagoblin/templates/mediagoblin/utils/prev_next.html:39 +#: mediagoblin/templates/mediagoblin/utils/prev_next.html:44 msgid "older" msgstr "" #: mediagoblin/templates/mediagoblin/utils/tags.html:20 -msgid "View more media tagged with" -msgstr "" - -#: mediagoblin/templates/mediagoblin/utils/tags.html:25 -msgid "or" +msgid "Tagged with" msgstr "" #: mediagoblin/tools/exif.py:68 @@ -645,25 +622,22 @@ msgstr "" msgid "I am sure I want to delete this" msgstr "Eg er sikker eg vil sletta dette" -#: mediagoblin/user_pages/views.py:155 +#: mediagoblin/user_pages/views.py:153 msgid "Oops, your comment was empty." msgstr "" -#: mediagoblin/user_pages/views.py:161 +#: mediagoblin/user_pages/views.py:159 msgid "Your comment has been posted!" msgstr "" -#: mediagoblin/user_pages/views.py:183 +#: mediagoblin/user_pages/views.py:185 msgid "You deleted the media." msgstr "" -#: mediagoblin/user_pages/views.py:190 +#: mediagoblin/user_pages/views.py:192 msgid "The media was not deleted because you didn't check that you were sure." msgstr "" -#: mediagoblin/user_pages/views.py:198 +#: mediagoblin/user_pages/views.py:200 msgid "You are about to delete another user's media. Proceed with caution." -msgstr "" -"Du er i ferd med Ã¥ sletta ein annan brukar sine mediefiler. TrÃ¥ varsamt." - - +msgstr "Du er i ferd med Ã¥ sletta ein annan brukar sine mediefiler. TrÃ¥ varsamt." diff --git a/mediagoblin/i18n/pt_BR/LC_MESSAGES/mediagoblin.mo b/mediagoblin/i18n/pt_BR/LC_MESSAGES/mediagoblin.mo Binary files differindex 6d2e6fc6..7644ab3e 100644 --- a/mediagoblin/i18n/pt_BR/LC_MESSAGES/mediagoblin.mo +++ b/mediagoblin/i18n/pt_BR/LC_MESSAGES/mediagoblin.mo diff --git a/mediagoblin/i18n/pt_BR/LC_MESSAGES/mediagoblin.po b/mediagoblin/i18n/pt_BR/LC_MESSAGES/mediagoblin.po index 14c54fb3..feddf376 100644 --- a/mediagoblin/i18n/pt_BR/LC_MESSAGES/mediagoblin.po +++ b/mediagoblin/i18n/pt_BR/LC_MESSAGES/mediagoblin.po @@ -8,11 +8,11 @@ msgid "" msgstr "" "Project-Id-Version: GNU MediaGoblin\n" -"Report-Msgid-Bugs-To: http://bugs.foocorp.net/projects/mediagoblin/issues\n" -"POT-Creation-Date: 2012-01-29 13:31-0600\n" -"PO-Revision-Date: 2012-01-29 19:29+0000\n" +"Report-Msgid-Bugs-To: http://issues.mediagoblin.org/\n" +"POT-Creation-Date: 2012-03-18 15:16-0500\n" +"PO-Revision-Date: 2012-03-18 20:14+0000\n" "Last-Translator: cwebber <cwebber@dustycloud.org>\n" -"Language-Team: Portuguese (Brazil) (http://www.transifex.net/projects/p/mediagoblin/team/pt_BR/)\n" +"Language-Team: Portuguese (Brazil) (http://www.transifex.net/projects/p/mediagoblin/language/pt_BR/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" @@ -20,7 +20,7 @@ msgstr "" "Language: pt_BR\n" "Plural-Forms: nplurals=2; plural=(n > 1)\n" -#: mediagoblin/processing.py:143 +#: mediagoblin/processing.py:154 msgid "Invalid file given for media type." msgstr "Arquivo inválido para esse tipo de mÃdia" @@ -40,56 +40,52 @@ msgstr "Endereço de email" msgid "Sorry, registration is disabled on this instance." msgstr "Desculpa, o registro está desativado neste momento." -#: mediagoblin/auth/views.py:73 +#: mediagoblin/auth/views.py:75 msgid "Sorry, a user with that name already exists." msgstr "Desculpe, um usuário com este nome já existe." -#: mediagoblin/auth/views.py:77 +#: mediagoblin/auth/views.py:79 msgid "Sorry, a user with that email address already exists." msgstr "Desculpe, um usuário com esse email já esta cadastrado" -#: mediagoblin/auth/views.py:180 +#: mediagoblin/auth/views.py:182 msgid "" "Your email address has been verified. You may now login, edit your profile, " "and submit images!" -msgstr "" -"O seu endereço de e-mail foi verificado. Você pode agora fazer login, editar" -" seu perfil, e enviar imagens!" +msgstr "O seu endereço de e-mail foi verificado. Você pode agora fazer login, editar seu perfil, e enviar imagens!" -#: mediagoblin/auth/views.py:186 +#: mediagoblin/auth/views.py:188 msgid "The verification key or user id is incorrect" msgstr "A chave de verificação ou nome usuário estão incorretos." -#: mediagoblin/auth/views.py:204 +#: mediagoblin/auth/views.py:206 msgid "You must be logged in so we know who to send the email to!" msgstr " " -#: mediagoblin/auth/views.py:212 +#: mediagoblin/auth/views.py:214 msgid "You've already verified your email address!" msgstr "Você já verifico seu email!" -#: mediagoblin/auth/views.py:225 +#: mediagoblin/auth/views.py:227 msgid "Resent your verification email." msgstr "O email de verificação foi reenviado." -#: mediagoblin/auth/views.py:260 +#: mediagoblin/auth/views.py:262 msgid "" "An email has been sent with instructions on how to change your password." msgstr "" -#: mediagoblin/auth/views.py:270 +#: mediagoblin/auth/views.py:272 msgid "" "Could not send password recovery email as your username is inactive or your " "account's email address has not been verified." -msgstr "" -"Não foi possÃvel enviar o email de recuperação de senha, pois seu nome de " -"usuário está inativo ou o email da sua conta não foi confirmado." +msgstr "Não foi possÃvel enviar o email de recuperação de senha, pois seu nome de usuário está inativo ou o email da sua conta não foi confirmado." -#: mediagoblin/auth/views.py:282 +#: mediagoblin/auth/views.py:284 msgid "Couldn't find someone with that username or email." msgstr "" -#: mediagoblin/auth/views.py:330 +#: mediagoblin/auth/views.py:332 msgid "You can now log in using your new password." msgstr "" @@ -132,6 +128,7 @@ msgid "" msgstr "" #: mediagoblin/edit/forms.py:44 mediagoblin/submit/forms.py:41 +#: mediagoblin/templates/mediagoblin/utils/license.html:20 msgid "License" msgstr "" @@ -155,27 +152,27 @@ msgstr "" msgid "New password" msgstr "" -#: mediagoblin/edit/views.py:68 +#: mediagoblin/edit/views.py:67 msgid "An entry with that slug already exists for this user." msgstr "Uma entrada com esse arquivo já existe para esse usuário" -#: mediagoblin/edit/views.py:92 +#: mediagoblin/edit/views.py:88 msgid "You are editing another user's media. Proceed with caution." msgstr "Você está editando a mÃdia de outro usuário. Tenha cuidado." -#: mediagoblin/edit/views.py:162 +#: mediagoblin/edit/views.py:158 msgid "You are editing a user's profile. Proceed with caution." msgstr "Você está editando um perfil de usuário. Tenha cuidado." -#: mediagoblin/edit/views.py:180 +#: mediagoblin/edit/views.py:174 msgid "Profile changes saved" msgstr "" -#: mediagoblin/edit/views.py:206 +#: mediagoblin/edit/views.py:200 msgid "Wrong password" msgstr "Senha errada" -#: mediagoblin/edit/views.py:222 +#: mediagoblin/edit/views.py:216 msgid "Account settings saved" msgstr "" @@ -195,7 +192,7 @@ msgstr "Arquivo" msgid "You must provide a file." msgstr "Você deve fornecer um arquivo." -#: mediagoblin/submit/views.py:158 +#: mediagoblin/submit/views.py:156 msgid "Woohoo! Submitted!" msgstr "Eba! Enviado!" @@ -215,9 +212,7 @@ msgstr "Aparentemente não existe uma página com esse endereço. Desculpe!" msgid "" "If you're sure the address is correct, maybe the page you're looking for has" " been moved or deleted." -msgstr "" -"Se você está certo de que o endereço está correto, talvez a página que " -"esteja procurando tenha sido apagada ou mudou de endereço" +msgstr "Se você está certo de que o endereço está correto, talvez a página que esteja procurando tenha sido apagada ou mudou de endereço" #: mediagoblin/templates/mediagoblin/base.html:46 msgid "MediaGoblin logo" @@ -245,10 +240,16 @@ msgstr "Entrar" #: mediagoblin/templates/mediagoblin/base.html:84 msgid "" "Powered by <a href=\"http://mediagoblin.org\">MediaGoblin</a>, a <a " -"href=\"http://gnu.org/\">GNU</a> project" +"href=\"http://gnu.org/\">GNU</a> project." +msgstr "" + +#: mediagoblin/templates/mediagoblin/base.html:87 +#, python-format +msgid "" +"Released under the <a " +"href=\"http://www.fsf.org/licensing/licenses/agpl-3.0.html\">AGPL</a>. <a " +"href=\"%(source_link)s\">Source code</a> available." msgstr "" -"Desenvolvido por <a href=\"http://mediagoblin.org\">MediaGoblin</a>, um " -"projeto <a href=\"http://gnu.org/\">GNU</a>" #: mediagoblin/templates/mediagoblin/root.html:24 msgid "Explore" @@ -314,15 +315,7 @@ msgid "" "\n" "If you think this is an error, just ignore this email and continue being\n" "a happy goblin!" -msgstr "" -"Olá %(username)s,\n" -"\n" -"para alterar sua senha do GNU MediaGoblin, abra a seguinte URL\n" -"no seu navegador web:\n" -"\n" -"%(verification_url)s\n" -"\n" -"Se você acha que isso é um erro, desconsidere esse email e continue sendo um goblin feliz" +msgstr "Olá %(username)s,\n\npara alterar sua senha do GNU MediaGoblin, abra a seguinte URL\nno seu navegador web:\n\n%(verification_url)s\n\nSe você acha que isso é um erro, desconsidere esse email e continue sendo um goblin feliz" #: mediagoblin/templates/mediagoblin/auth/login.html:30 msgid "Logging in failed!" @@ -357,12 +350,7 @@ msgid "" "your web browser:\n" "\n" "%(verification_url)s" -msgstr "" -"Olá %(username)s,\n" -"\n" -"Para ativar sua conta GNU MediaGoblin, visite este endereço no seu navegador:\n" -"\n" -"%(verification_url)s" +msgstr "Olá %(username)s,\n\nPara ativar sua conta GNU MediaGoblin, visite este endereço no seu navegador:\n\n%(verification_url)s" #: mediagoblin/templates/mediagoblin/edit/edit.html:29 #, python-format @@ -397,18 +385,18 @@ msgid "Media tagged with: %(tag_name)s" msgstr "" #: mediagoblin/templates/mediagoblin/media_displays/ascii.html:34 -#: mediagoblin/templates/mediagoblin/media_displays/video.html:46 +#: mediagoblin/templates/mediagoblin/media_displays/video.html:53 msgid "Original" msgstr "Original" -#: mediagoblin/templates/mediagoblin/media_displays/video.html:33 +#: mediagoblin/templates/mediagoblin/media_displays/video.html:40 msgid "" "Sorry, this video will not work because \n" "\t your web browser does not support HTML5 \n" "\t video." msgstr "" -#: mediagoblin/templates/mediagoblin/media_displays/video.html:36 +#: mediagoblin/templates/mediagoblin/media_displays/video.html:43 msgid "" "You can get a modern web browser that \n" "\t can play this video at <a href=\"http://getfirefox.com\">\n" @@ -433,55 +421,43 @@ msgstr "" msgid "<a href=\"%(user_url)s\">%(username)s</a>'s media" msgstr "MÃdia de <a href=\"%(user_url)s\"> %(username)s </a> " -#: mediagoblin/templates/mediagoblin/user_pages/media.html:72 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:46 #, python-format -msgid "Added on %(date)s." +msgid "â– Browsing media by <a href=\"%(user_url)s\">%(username)s</a>" msgstr "" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:81 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:85 msgid "Edit" msgstr "Editar" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:85 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:89 msgid "Delete" msgstr "Apagar" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:91 -#, python-format -msgid "%(comment_count)s comment" +#: mediagoblin/templates/mediagoblin/user_pages/media.html:124 +msgid "Add a comment" msgstr "" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:93 -#, python-format -msgid "%(comment_count)s comments" -msgstr "" - -#: mediagoblin/templates/mediagoblin/user_pages/media.html:95 -msgid "No comments yet." -msgstr "" - -#: mediagoblin/templates/mediagoblin/user_pages/media.html:103 -msgid "Add one" -msgstr "" - -#: mediagoblin/templates/mediagoblin/user_pages/media.html:112 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:133 msgid "" "You can use <a " "href=\"http://daringfireball.net/projects/markdown/basics\">Markdown</a> for" " formatting." msgstr "" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:116 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:137 msgid "Add this comment" msgstr "" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:138 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:159 msgid "at" msgstr "" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:153 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:174 #, python-format -msgid "<p>â– Browsing media by <a href=\"%(user_url)s\">%(username)s</a></p>" +msgid "" +"<h3>Added on</h3>\n" +" <p>%(date)s</p>" msgstr "" #: mediagoblin/templates/mediagoblin/user_pages/media_confirm_delete.html:30 @@ -490,8 +466,8 @@ msgid "Really delete %(title)s?" msgstr "Realmente apagar %(title)s ?" #: mediagoblin/templates/mediagoblin/user_pages/media_confirm_delete.html:50 -msgid "Delete Permanently" -msgstr "Apagar permanentemente" +msgid "Delete permanently" +msgstr "" #: mediagoblin/templates/mediagoblin/user_pages/processing_panel.html:22 msgid "Media processing panel" @@ -500,8 +476,7 @@ msgstr "Painel de processamento de mÃdia" #: mediagoblin/templates/mediagoblin/user_pages/processing_panel.html:25 msgid "" "You can track the state of media being processed for your gallery here." -msgstr "" -"Você pode verificar como a mÃdia esta sendo processada para sua galeria aqui" +msgstr "Você pode verificar como a mÃdia esta sendo processada para sua galeria aqui" #: mediagoblin/templates/mediagoblin/user_pages/processing_panel.html:28 msgid "Media in-processing" @@ -551,18 +526,14 @@ msgstr "Reenviar email de verificação" msgid "" "Someone has registered an account with this username, but it still has to be" " activated." -msgstr "" -"Alguém registrou uma conta com esse nome de usuário, mas ainda precisa ser " -"ativada." +msgstr "Alguém registrou uma conta com esse nome de usuário, mas ainda precisa ser ativada." #: mediagoblin/templates/mediagoblin/user_pages/user.html:79 #, python-format msgid "" "If you are that person but you've lost your verification email, you can <a " "href=\"%(login_url)s\">log in</a> and resend it." -msgstr "" -"Se você é essa pessoa, mas você perdeu seu e-mail de verificação, você pode " -"<a href=\"%(login_url)s\">efetuar login</a> e reenviá-la." +msgstr "Se você é essa pessoa, mas você perdeu seu e-mail de verificação, você pode <a href=\"%(login_url)s\">efetuar login</a> e reenviá-la." #: mediagoblin/templates/mediagoblin/user_pages/user.html:96 msgid "Here's a spot to tell others about yourself." @@ -590,9 +561,7 @@ msgstr "Ver todas as mÃdias de %(username)s" msgid "" "This is where your media will appear, but you don't seem to have added " "anything yet." -msgstr "" -"Aqui é onde sua mÃdia vai aparecer, mas parece que você não adicionou nada " -"ainda." +msgstr "Aqui é onde sua mÃdia vai aparecer, mas parece que você não adicionou nada ainda." #: mediagoblin/templates/mediagoblin/user_pages/user.html:163 #: mediagoblin/templates/mediagoblin/utils/object_gallery.html:72 @@ -607,8 +576,13 @@ msgstr "Ãcone feed" msgid "Atom feed" msgstr "Atom feed" -#: mediagoblin/templates/mediagoblin/utils/license.html:21 -msgid "License:" +#: mediagoblin/templates/mediagoblin/utils/geolocation_map.html:25 +msgid "Location" +msgstr "" + +#: mediagoblin/templates/mediagoblin/utils/geolocation_map.html:38 +#, python-format +msgid "View on <a href=\"%(osm_url)s\">OpenStreetMap</a>" msgstr "" #: mediagoblin/templates/mediagoblin/utils/license.html:25 @@ -627,22 +601,18 @@ msgstr "" msgid "Go to page:" msgstr "Ir a página:" -#: mediagoblin/templates/mediagoblin/utils/prev_next.html:27 -#: mediagoblin/templates/mediagoblin/utils/prev_next.html:32 +#: mediagoblin/templates/mediagoblin/utils/prev_next.html:28 +#: mediagoblin/templates/mediagoblin/utils/prev_next.html:33 msgid "newer" msgstr "" -#: mediagoblin/templates/mediagoblin/utils/prev_next.html:38 -#: mediagoblin/templates/mediagoblin/utils/prev_next.html:43 +#: mediagoblin/templates/mediagoblin/utils/prev_next.html:39 +#: mediagoblin/templates/mediagoblin/utils/prev_next.html:44 msgid "older" msgstr "" #: mediagoblin/templates/mediagoblin/utils/tags.html:20 -msgid "View more media tagged with" -msgstr "" - -#: mediagoblin/templates/mediagoblin/utils/tags.html:25 -msgid "or" +msgid "Tagged with" msgstr "" #: mediagoblin/tools/exif.py:68 @@ -653,24 +623,22 @@ msgstr "" msgid "I am sure I want to delete this" msgstr "Eu tenho certeza de que quero pagar isso" -#: mediagoblin/user_pages/views.py:155 +#: mediagoblin/user_pages/views.py:153 msgid "Oops, your comment was empty." msgstr "Opa, seu comentáio estava vazio." -#: mediagoblin/user_pages/views.py:161 +#: mediagoblin/user_pages/views.py:159 msgid "Your comment has been posted!" msgstr "Seu comentário foi postado!" -#: mediagoblin/user_pages/views.py:183 +#: mediagoblin/user_pages/views.py:185 msgid "You deleted the media." msgstr "Você deletou a mÃdia." -#: mediagoblin/user_pages/views.py:190 +#: mediagoblin/user_pages/views.py:192 msgid "The media was not deleted because you didn't check that you were sure." msgstr "" -#: mediagoblin/user_pages/views.py:198 +#: mediagoblin/user_pages/views.py:200 msgid "You are about to delete another user's media. Proceed with caution." msgstr "Você vai apagar uma mÃdia de outro usuário. Tenha cuidado." - - diff --git a/mediagoblin/i18n/ro/LC_MESSAGES/mediagoblin.mo b/mediagoblin/i18n/ro/LC_MESSAGES/mediagoblin.mo Binary files differindex 226474af..11924a75 100644 --- a/mediagoblin/i18n/ro/LC_MESSAGES/mediagoblin.mo +++ b/mediagoblin/i18n/ro/LC_MESSAGES/mediagoblin.mo diff --git a/mediagoblin/i18n/ro/LC_MESSAGES/mediagoblin.po b/mediagoblin/i18n/ro/LC_MESSAGES/mediagoblin.po index 9139e59d..4cc21d9d 100644 --- a/mediagoblin/i18n/ro/LC_MESSAGES/mediagoblin.po +++ b/mediagoblin/i18n/ro/LC_MESSAGES/mediagoblin.po @@ -8,9 +8,9 @@ msgid "" msgstr "" "Project-Id-Version: GNU MediaGoblin\n" -"Report-Msgid-Bugs-To: http://bugs.foocorp.net/projects/mediagoblin/issues\n" -"POT-Creation-Date: 2012-01-29 13:31-0600\n" -"PO-Revision-Date: 2012-01-29 19:29+0000\n" +"Report-Msgid-Bugs-To: http://issues.mediagoblin.org/\n" +"POT-Creation-Date: 2012-03-18 15:16-0500\n" +"PO-Revision-Date: 2012-03-18 20:14+0000\n" "Last-Translator: cwebber <cwebber@dustycloud.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n" "MIME-Version: 1.0\n" @@ -20,7 +20,7 @@ msgstr "" "Language: ro\n" "Plural-Forms: nplurals=3; plural=(n==1?0:(((n%100>19)||((n%100==0)&&(n!=0)))?2:1))\n" -#: mediagoblin/processing.py:143 +#: mediagoblin/processing.py:154 msgid "Invalid file given for media type." msgstr "Formatul fiÈ™ierului nu corespunde cu tipul de media selectat." @@ -40,57 +40,52 @@ msgstr "Adresa de e-mail" msgid "Sorry, registration is disabled on this instance." msgstr "Ne pare rău, dar înscrierile sunt dezactivate pe acest server." -#: mediagoblin/auth/views.py:73 +#: mediagoblin/auth/views.py:75 msgid "Sorry, a user with that name already exists." msgstr "Ne pare rău, există deja un utilizator cu acelaÈ™i nume." -#: mediagoblin/auth/views.py:77 +#: mediagoblin/auth/views.py:79 msgid "Sorry, a user with that email address already exists." msgstr "Există deja un utilizator înregistrat cu această adresă de e-mail." -#: mediagoblin/auth/views.py:180 +#: mediagoblin/auth/views.py:182 msgid "" "Your email address has been verified. You may now login, edit your profile, " "and submit images!" -msgstr "" -"Adresa ta de e-mail a fost verificată. PoÈ›i să te autentifici, să îți " -"completezi profilul È™i să trimiÈ›i imagini!" +msgstr "Adresa ta de e-mail a fost verificată. PoÈ›i să te autentifici, să îți completezi profilul È™i să trimiÈ›i imagini!" -#: mediagoblin/auth/views.py:186 +#: mediagoblin/auth/views.py:188 msgid "The verification key or user id is incorrect" msgstr "Cheie de verificare sau user ID incorect." -#: mediagoblin/auth/views.py:204 +#: mediagoblin/auth/views.py:206 msgid "You must be logged in so we know who to send the email to!" msgstr "Trebuie să fii autentificat ca să È™tim cui să trimitem mesajul!" -#: mediagoblin/auth/views.py:212 +#: mediagoblin/auth/views.py:214 msgid "You've already verified your email address!" msgstr "Adresa ta de e-mail a fost deja verificată!" -#: mediagoblin/auth/views.py:225 +#: mediagoblin/auth/views.py:227 msgid "Resent your verification email." msgstr "E-mail-ul de verificare a fost retrimis." -#: mediagoblin/auth/views.py:260 +#: mediagoblin/auth/views.py:262 msgid "" "An email has been sent with instructions on how to change your password." msgstr "S-a trimis un e-mail cu instrucÈ›iuni pentru schimbarea parolei." -#: mediagoblin/auth/views.py:270 +#: mediagoblin/auth/views.py:272 msgid "" "Could not send password recovery email as your username is inactive or your " "account's email address has not been verified." -msgstr "" -"E-mailul pentru recuperarea parolei nu a putut fi trimis deoarece contul tău" -" e inactiv sau adresa ta de e-mail nu a fost verificată." +msgstr "E-mailul pentru recuperarea parolei nu a putut fi trimis deoarece contul tău e inactiv sau adresa ta de e-mail nu a fost verificată." -#: mediagoblin/auth/views.py:282 +#: mediagoblin/auth/views.py:284 msgid "Couldn't find someone with that username or email." -msgstr "" -"Nu s-a găsit nicio persoană cu acel nume de utilizator sau adresă de e-mail." +msgstr "Nu s-a găsit nicio persoană cu acel nume de utilizator sau adresă de e-mail." -#: mediagoblin/auth/views.py:330 +#: mediagoblin/auth/views.py:332 msgid "You can now log in using your new password." msgstr "Acum te poÈ›i autentifica cu noua parolă." @@ -108,10 +103,7 @@ msgid "" "You can use\n" " <a href=\"http://daringfireball.net/projects/markdown/basics\">\n" " Markdown</a> for formatting." -msgstr "" -"PoÈ›i folosi\n" -" <a href=\"http://daringfireball.net/projects/markdown/basics\">\n" -" Markdown</a> pentru formatare." +msgstr "PoÈ›i folosi\n <a href=\"http://daringfireball.net/projects/markdown/basics\">\n Markdown</a> pentru formatare." #: mediagoblin/edit/forms.py:33 mediagoblin/submit/forms.py:36 msgid "Tags" @@ -133,13 +125,12 @@ msgstr "Identificatorul nu poate să lipsească" msgid "" "The title part of this media's address. You usually don't need to change " "this." -msgstr "" -"Partea corespunzătoare titlului din adresa acestui fiÈ™ier media. De regulă " -"poate fi lăsată nemodificată." +msgstr "Partea corespunzătoare titlului din adresa acestui fiÈ™ier media. De regulă poate fi lăsată nemodificată." #: mediagoblin/edit/forms.py:44 mediagoblin/submit/forms.py:41 +#: mediagoblin/templates/mediagoblin/utils/license.html:20 msgid "License" -msgstr "" +msgstr "LicenÈ›a" #: mediagoblin/edit/forms.py:50 msgid "Bio" @@ -155,35 +146,33 @@ msgstr "Vechea parolă" #: mediagoblin/edit/forms.py:65 msgid "Enter your old password to prove you own this account." -msgstr "" -"Introdu vechea parolă pentru a demonstra că eÈ™ti titularul acestui cont." +msgstr "Introdu vechea parolă pentru a demonstra că eÈ™ti titularul acestui cont." #: mediagoblin/edit/forms.py:68 msgid "New password" msgstr "Noua parolă" -#: mediagoblin/edit/views.py:68 +#: mediagoblin/edit/views.py:67 msgid "An entry with that slug already exists for this user." -msgstr "" -"Există deja un entry cu acelaÈ™i identificator pentru acest utilizator." +msgstr "Există deja un entry cu acelaÈ™i identificator pentru acest utilizator." -#: mediagoblin/edit/views.py:92 +#: mediagoblin/edit/views.py:88 msgid "You are editing another user's media. Proceed with caution." msgstr "Editezi fiÈ™ierul unui alt utilizator. Se recomandă prudență." -#: mediagoblin/edit/views.py:162 +#: mediagoblin/edit/views.py:158 msgid "You are editing a user's profile. Proceed with caution." msgstr "Editezi profilul unui utilizator. Se recomandă prudență." -#: mediagoblin/edit/views.py:180 +#: mediagoblin/edit/views.py:174 msgid "Profile changes saved" msgstr "Modificările profilului au fost salvate" -#: mediagoblin/edit/views.py:206 +#: mediagoblin/edit/views.py:200 msgid "Wrong password" msgstr "Parolă incorectă" -#: mediagoblin/edit/views.py:222 +#: mediagoblin/edit/views.py:216 msgid "Account settings saved" msgstr "Setările pentru acest cont au fost salvate" @@ -203,7 +192,7 @@ msgstr "FiÈ™ier" msgid "You must provide a file." msgstr "Trebuie să selectezi un fiÈ™ier." -#: mediagoblin/submit/views.py:158 +#: mediagoblin/submit/views.py:156 msgid "Woohoo! Submitted!" msgstr "Ura! Trimis!" @@ -223,9 +212,7 @@ msgstr "Nu există nicio pagină la această adresă. Ne pare rău!" msgid "" "If you're sure the address is correct, maybe the page you're looking for has" " been moved or deleted." -msgstr "" -"Dacă eÈ™ti sigur că adresa e corectă, poate că pagina pe care o cauÈ›i a fost " -"mutată sau È™tearsă." +msgstr "Dacă eÈ™ti sigur că adresa e corectă, poate că pagina pe care o cauÈ›i a fost mutată sau È™tearsă." #: mediagoblin/templates/mediagoblin/base.html:46 msgid "MediaGoblin logo" @@ -253,10 +240,16 @@ msgstr "Autentificare" #: mediagoblin/templates/mediagoblin/base.html:84 msgid "" "Powered by <a href=\"http://mediagoblin.org\">MediaGoblin</a>, a <a " -"href=\"http://gnu.org/\">GNU</a> project" +"href=\"http://gnu.org/\">GNU</a> project." +msgstr "" + +#: mediagoblin/templates/mediagoblin/base.html:87 +#, python-format +msgid "" +"Released under the <a " +"href=\"http://www.fsf.org/licensing/licenses/agpl-3.0.html\">AGPL</a>. <a " +"href=\"%(source_link)s\">Source code</a> available." msgstr "" -"Construit cu <a href=\"http://mediagoblin.org\">MediaGoblin</a>, un proiect " -"<a href=\"http://gnu.org/\">GNU</a>" #: mediagoblin/templates/mediagoblin/root.html:24 msgid "Explore" @@ -270,17 +263,13 @@ msgstr "Salut, bine ai venit pe acest site MediaGoblin!" msgid "" "This site is running <a href=\"http://mediagoblin.org\">MediaGoblin</a>, an " "extraordinarily great piece of media hosting software." -msgstr "" -"Acest site foloseÈ™te <a href=\"http://mediagoblin.org\">MediaGoblin</a>, un " -"software excepÈ›ional pentru găzduirea fiÈ™ierelor media." +msgstr "Acest site foloseÈ™te <a href=\"http://mediagoblin.org\">MediaGoblin</a>, un software excepÈ›ional pentru găzduirea fiÈ™ierelor media." #: mediagoblin/templates/mediagoblin/root.html:29 msgid "" "To add your own media, place comments, save your favourites and more, you " "can log in with your MediaGoblin account." -msgstr "" -"Ca să adăugi propriile tale fiÈ™iere, să scrii comentarii, să salvezi " -"favoritele tale È™i multe altele, autentifică-te cu contul tău MediaGoblin." +msgstr "Ca să adăugi propriile tale fiÈ™iere, să scrii comentarii, să salvezi favoritele tale È™i multe altele, autentifică-te cu contul tău MediaGoblin." #: mediagoblin/templates/mediagoblin/root.html:31 msgid "Don't have one yet? It's easy!" @@ -292,10 +281,7 @@ msgid "" "<a class=\"button_action_highlight\" href=\"%(register_url)s\">Create an account at this site</a>\n" " or\n" " <a class=\"button_action\" href=\"http://wiki.mediagoblin.org/HackingHowto\">Set up MediaGoblin on your own server</a>" -msgstr "" -"<a class=\"button_action_highlight\" href=\"%(register_url)s\">Creează un cont pe acest site</a>\n" -" sau\n" -" <a class=\"button_action\" href=\"http://wiki.mediagoblin.org/HackingHowto\">Instalează MediaGoblin pe serverul tău</a>" +msgstr "<a class=\"button_action_highlight\" href=\"%(register_url)s\">Creează un cont pe acest site</a>\n sau\n <a class=\"button_action\" href=\"http://wiki.mediagoblin.org/HackingHowto\">Instalează MediaGoblin pe serverul tău</a>" #: mediagoblin/templates/mediagoblin/root.html:40 msgid "Most recent media" @@ -329,14 +315,7 @@ msgid "" "\n" "If you think this is an error, just ignore this email and continue being\n" "a happy goblin!" -msgstr "" -"Bună, %(username)s\n" -"\n" -"Pentru a schimba parola ta la GNU MediaGoblin, accesează adresa următoare:\n" -"\n" -"%(verification_url)s\n" -"\n" -"Dacă ai primit acest mesaj din greÈ™eală, ignoră-l È™i fii mai departe un elf fericit!" +msgstr "Bună, %(username)s\n\nPentru a schimba parola ta la GNU MediaGoblin, accesează adresa următoare:\n\n%(verification_url)s\n\nDacă ai primit acest mesaj din greÈ™eală, ignoră-l È™i fii mai departe un elf fericit!" #: mediagoblin/templates/mediagoblin/auth/login.html:30 msgid "Logging in failed!" @@ -371,12 +350,7 @@ msgid "" "your web browser:\n" "\n" "%(verification_url)s" -msgstr "" -"Bună, %(username)s,\n" -"\n" -"pentru activarea contului tău la GNU MediaGoblin, accesează adresa următoare:\n" -"\n" -"%(verification_url)s" +msgstr "Bună, %(username)s,\n\npentru activarea contului tău la GNU MediaGoblin, accesează adresa următoare:\n\n%(verification_url)s" #: mediagoblin/templates/mediagoblin/edit/edit.html:29 #, python-format @@ -411,28 +385,23 @@ msgid "Media tagged with: %(tag_name)s" msgstr "FiÈ™ier etichetat cu tag-urile: %(tag_name)s" #: mediagoblin/templates/mediagoblin/media_displays/ascii.html:34 -#: mediagoblin/templates/mediagoblin/media_displays/video.html:46 +#: mediagoblin/templates/mediagoblin/media_displays/video.html:53 msgid "Original" msgstr "Original" -#: mediagoblin/templates/mediagoblin/media_displays/video.html:33 +#: mediagoblin/templates/mediagoblin/media_displays/video.html:40 msgid "" "Sorry, this video will not work because \n" "\t your web browser does not support HTML5 \n" "\t video." -msgstr "" -"Ne pare rău, această înregistrare video nu funcÈ›ionează deoarece \n" -"<span class=\"whitespace other\" title=\"Tab\">»</span> browserul tău nu este compatibil cu funcÈ›ia video din HTML5." +msgstr "Ne pare rău, această înregistrare video nu funcÈ›ionează deoarece \n<span class=\"whitespace other\" title=\"Tab\">»</span> browserul tău nu este compatibil cu funcÈ›ia video din HTML5." -#: mediagoblin/templates/mediagoblin/media_displays/video.html:36 +#: mediagoblin/templates/mediagoblin/media_displays/video.html:43 msgid "" "You can get a modern web browser that \n" "\t can play this video at <a href=\"http://getfirefox.com\">\n" "\t http://getfirefox.com</a>!" -msgstr "" -"PoÈ›i lua un browser modern cu care\n" -"<span class=\"whitespace other\" title=\"Tab\">»</span> care poÈ›i vedea această înregistrare video de la <a href=\"http://getfirefox.com\">\n" -"<span class=\"whitespace other\" title=\"Tab\">»</span> http://getfirefox.com</a>!" +msgstr "PoÈ›i lua un browser modern cu care\n<span class=\"whitespace other\" title=\"Tab\">»</span> care poÈ›i vedea această înregistrare video de la <a href=\"http://getfirefox.com\">\n<span class=\"whitespace other\" title=\"Tab\">»</span> http://getfirefox.com</a>!" #: mediagoblin/templates/mediagoblin/submit/start.html:26 msgid "Add your media" @@ -452,57 +421,44 @@ msgstr "FiÈ™ierele lui %(username)s" msgid "<a href=\"%(user_url)s\">%(username)s</a>'s media" msgstr "FiÈ™ierele media ale lui <a href=\"%(user_url)s\">%(username)s</a>" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:72 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:46 #, python-format -msgid "Added on %(date)s." -msgstr "Adăugat la data %(date)s." +msgid "â– Browsing media by <a href=\"%(user_url)s\">%(username)s</a>" +msgstr "" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:81 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:85 msgid "Edit" msgstr "Editare" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:85 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:89 msgid "Delete" msgstr "Șterge" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:91 -#, python-format -msgid "%(comment_count)s comment" -msgstr "%(comment_count)s comentariu" - -#: mediagoblin/templates/mediagoblin/user_pages/media.html:93 -#, python-format -msgid "%(comment_count)s comments" -msgstr "%(comment_count)s comentarii" - -#: mediagoblin/templates/mediagoblin/user_pages/media.html:95 -msgid "No comments yet." -msgstr "Deocamdată nu există comentarii." - -#: mediagoblin/templates/mediagoblin/user_pages/media.html:103 -msgid "Add one" -msgstr "Trimite unul" +#: mediagoblin/templates/mediagoblin/user_pages/media.html:124 +msgid "Add a comment" +msgstr "" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:112 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:133 msgid "" "You can use <a " "href=\"http://daringfireball.net/projects/markdown/basics\">Markdown</a> for" " formatting." -msgstr "" +msgstr "PoÈ›i folosi <a href=\"http://daringfireball.net/projects/markdown/basics\">Markdown</a> pentru formatare." -#: mediagoblin/templates/mediagoblin/user_pages/media.html:116 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:137 msgid "Add this comment" msgstr "Trimite acest comentariu" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:138 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:159 msgid "at" msgstr "la" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:153 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:174 #, python-format -msgid "<p>â– Browsing media by <a href=\"%(user_url)s\">%(username)s</a></p>" +msgid "" +"<h3>Added on</h3>\n" +" <p>%(date)s</p>" msgstr "" -"<p>â– FiÈ™ierele media ale lui <a href=\"%(user_url)s\">%(username)s</a></p>" #: mediagoblin/templates/mediagoblin/user_pages/media_confirm_delete.html:30 #, python-format @@ -510,8 +466,8 @@ msgid "Really delete %(title)s?" msgstr "Sigur doreÈ™ti să È™tergi %(title)s?" #: mediagoblin/templates/mediagoblin/user_pages/media_confirm_delete.html:50 -msgid "Delete Permanently" -msgstr "Șterge definitiv" +msgid "Delete permanently" +msgstr "" #: mediagoblin/templates/mediagoblin/user_pages/processing_panel.html:22 msgid "Media processing panel" @@ -570,18 +526,14 @@ msgstr "Retrimite mesajul de verificare" msgid "" "Someone has registered an account with this username, but it still has to be" " activated." -msgstr "" -"Cineva a înregistrat un cont cu acest nume de utilizator, dar contul nu a " -"fost încă activat." +msgstr "Cineva a înregistrat un cont cu acest nume de utilizator, dar contul nu a fost încă activat." #: mediagoblin/templates/mediagoblin/user_pages/user.html:79 #, python-format msgid "" "If you are that person but you've lost your verification email, you can <a " "href=\"%(login_url)s\">log in</a> and resend it." -msgstr "" -"Dacă tu eÈ™ti persoana respectivă È™i nu mai ai e-mail-ul de verificare, poÈ›i " -"să te <a href=\"%(login_url)s\">autentifici</a> pentru a-l retrimite." +msgstr "Dacă tu eÈ™ti persoana respectivă È™i nu mai ai e-mail-ul de verificare, poÈ›i să te <a href=\"%(login_url)s\">autentifici</a> pentru a-l retrimite." #: mediagoblin/templates/mediagoblin/user_pages/user.html:96 msgid "Here's a spot to tell others about yourself." @@ -609,9 +561,7 @@ msgstr "Vezi toate fiÈ™ierele media ale lui %(username)s" msgid "" "This is where your media will appear, but you don't seem to have added " "anything yet." -msgstr "" -"Aici vor apărea fiÈ™ierele tale media, dar se pare că încă nu ai trimis " -"nimic." +msgstr "Aici vor apărea fiÈ™ierele tale media, dar se pare că încă nu ai trimis nimic." #: mediagoblin/templates/mediagoblin/user_pages/user.html:163 #: mediagoblin/templates/mediagoblin/utils/object_gallery.html:72 @@ -626,13 +576,18 @@ msgstr "icon feed" msgid "Atom feed" msgstr "feed Atom" -#: mediagoblin/templates/mediagoblin/utils/license.html:21 -msgid "License:" +#: mediagoblin/templates/mediagoblin/utils/geolocation_map.html:25 +msgid "Location" +msgstr "" + +#: mediagoblin/templates/mediagoblin/utils/geolocation_map.html:38 +#, python-format +msgid "View on <a href=\"%(osm_url)s\">OpenStreetMap</a>" msgstr "" #: mediagoblin/templates/mediagoblin/utils/license.html:25 msgid "All rights reserved" -msgstr "" +msgstr "Toate drepturile rezervate" #: mediagoblin/templates/mediagoblin/utils/pagination.html:39 msgid "↠Newer" @@ -646,52 +601,44 @@ msgstr "Mai vechi →" msgid "Go to page:" msgstr "Salt la pagina:" -#: mediagoblin/templates/mediagoblin/utils/prev_next.html:27 -#: mediagoblin/templates/mediagoblin/utils/prev_next.html:32 +#: mediagoblin/templates/mediagoblin/utils/prev_next.html:28 +#: mediagoblin/templates/mediagoblin/utils/prev_next.html:33 msgid "newer" msgstr "mai noi" -#: mediagoblin/templates/mediagoblin/utils/prev_next.html:38 -#: mediagoblin/templates/mediagoblin/utils/prev_next.html:43 +#: mediagoblin/templates/mediagoblin/utils/prev_next.html:39 +#: mediagoblin/templates/mediagoblin/utils/prev_next.html:44 msgid "older" msgstr "mai vechi" #: mediagoblin/templates/mediagoblin/utils/tags.html:20 -msgid "View more media tagged with" -msgstr "Vezi È™i alte fiÈ™iere etichetate cu tag-ul" - -#: mediagoblin/templates/mediagoblin/utils/tags.html:25 -msgid "or" -msgstr "sau" +msgid "Tagged with" +msgstr "" #: mediagoblin/tools/exif.py:68 msgid "Could not read the image file." -msgstr "" +msgstr "FiÈ™ierul cu imaginea nu a putut fi citit." #: mediagoblin/user_pages/forms.py:30 msgid "I am sure I want to delete this" msgstr "Sunt sigur că doresc să È™terg" -#: mediagoblin/user_pages/views.py:155 +#: mediagoblin/user_pages/views.py:153 msgid "Oops, your comment was empty." msgstr "Hopa, ai uitat să scrii comentariul." -#: mediagoblin/user_pages/views.py:161 +#: mediagoblin/user_pages/views.py:159 msgid "Your comment has been posted!" msgstr "Comentariul tău a fost trimis!" -#: mediagoblin/user_pages/views.py:183 +#: mediagoblin/user_pages/views.py:185 msgid "You deleted the media." msgstr "Ai È™ters acest fiÈ™ier" -#: mediagoblin/user_pages/views.py:190 +#: mediagoblin/user_pages/views.py:192 msgid "The media was not deleted because you didn't check that you were sure." msgstr "FiÈ™ierul nu a fost È™ters deoarece nu ai confirmat că eÈ™ti sigur." -#: mediagoblin/user_pages/views.py:198 +#: mediagoblin/user_pages/views.py:200 msgid "You are about to delete another user's media. Proceed with caution." -msgstr "" -"Urmează să È™tergi fiÈ™ierele media ale unui alt utilizator. Se recomandă " -"prudență." - - +msgstr "Urmează să È™tergi fiÈ™ierele media ale unui alt utilizator. Se recomandă prudență." diff --git a/mediagoblin/i18n/ru/LC_MESSAGES/mediagoblin.mo b/mediagoblin/i18n/ru/LC_MESSAGES/mediagoblin.mo Binary files differindex dd7735fd..3ccb7051 100644 --- a/mediagoblin/i18n/ru/LC_MESSAGES/mediagoblin.mo +++ b/mediagoblin/i18n/ru/LC_MESSAGES/mediagoblin.mo diff --git a/mediagoblin/i18n/ru/LC_MESSAGES/mediagoblin.po b/mediagoblin/i18n/ru/LC_MESSAGES/mediagoblin.po index d895f3bf..4985f27e 100644 --- a/mediagoblin/i18n/ru/LC_MESSAGES/mediagoblin.po +++ b/mediagoblin/i18n/ru/LC_MESSAGES/mediagoblin.po @@ -8,9 +8,9 @@ msgid "" msgstr "" "Project-Id-Version: GNU MediaGoblin\n" "Report-Msgid-Bugs-To: http://issues.mediagoblin.org/\n" -"POT-Creation-Date: 2012-01-29 13:47-0600\n" -"PO-Revision-Date: 2012-02-05 21:04+0000\n" -"Last-Translator: aleksejrs <deletesoftware@yandex.ru>\n" +"POT-Creation-Date: 2012-03-18 15:16-0500\n" +"PO-Revision-Date: 2012-03-18 20:14+0000\n" +"Last-Translator: cwebber <cwebber@dustycloud.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -19,7 +19,7 @@ msgstr "" "Language: ru\n" "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)\n" -#: mediagoblin/processing.py:143 +#: mediagoblin/processing.py:154 msgid "Invalid file given for media type." msgstr "Ðеправильный формат файла." @@ -39,60 +39,52 @@ msgstr "ÐÐ´Ñ€ÐµÑ Ñлектронной почты" msgid "Sorry, registration is disabled on this instance." msgstr "Извините, на Ñтом разделе региÑÑ‚Ñ€Ð°Ñ†Ð¸Ñ Ð·Ð°Ð¿Ñ€ÐµÑ‰ÐµÐ½Ð°." -#: mediagoblin/auth/views.py:73 +#: mediagoblin/auth/views.py:75 msgid "Sorry, a user with that name already exists." msgstr "Извините, пользователь Ñ Ñтим именем уже зарегиÑтрирован." -#: mediagoblin/auth/views.py:77 +#: mediagoblin/auth/views.py:79 msgid "Sorry, a user with that email address already exists." -msgstr "" -"Сожалеем, но на Ñтот Ð°Ð´Ñ€ÐµÑ Ñлектронной почты уже зарегиÑтрирована Ð´Ñ€ÑƒÐ³Ð°Ñ " -"ÑƒÑ‡Ñ‘Ñ‚Ð½Ð°Ñ Ð·Ð°Ð¿Ð¸ÑÑŒ." +msgstr "Сожалеем, но на Ñтот Ð°Ð´Ñ€ÐµÑ Ñлектронной почты уже зарегиÑтрирована Ð´Ñ€ÑƒÐ³Ð°Ñ ÑƒÑ‡Ñ‘Ñ‚Ð½Ð°Ñ Ð·Ð°Ð¿Ð¸ÑÑŒ." -#: mediagoblin/auth/views.py:180 +#: mediagoblin/auth/views.py:182 msgid "" "Your email address has been verified. You may now login, edit your profile, " "and submit images!" -msgstr "" -"ÐÐ´Ñ€ÐµÑ Ð²Ð°ÑˆÐµÐ¹ Ñлектронной потвержден. Ð’Ñ‹ теперь можете войти и начать " -"редактировать Ñвой профиль и загружать новые изображениÑ!" +msgstr "ÐÐ´Ñ€ÐµÑ Ð²Ð°ÑˆÐµÐ¹ Ñлектронной потвержден. Ð’Ñ‹ теперь можете войти и начать редактировать Ñвой профиль и загружать новые изображениÑ!" -#: mediagoblin/auth/views.py:186 +#: mediagoblin/auth/views.py:188 msgid "The verification key or user id is incorrect" msgstr "Ðеверный ключ проверки или идентификатор пользователÑ" -#: mediagoblin/auth/views.py:204 +#: mediagoblin/auth/views.py:206 msgid "You must be logged in so we know who to send the email to!" msgstr "Вам надо предÑтавитьÑÑ, чтобы мы знали, кому отправлÑть Ñообщение!" -#: mediagoblin/auth/views.py:212 +#: mediagoblin/auth/views.py:214 msgid "You've already verified your email address!" msgstr "Ð’Ñ‹ уже потвердили Ñвой Ð°Ð´Ñ€ÐµÑ Ñлектронной почты!" -#: mediagoblin/auth/views.py:225 +#: mediagoblin/auth/views.py:227 msgid "Resent your verification email." msgstr "ПереÑлать Ñообщение Ñ Ð¿Ð¾Ð´Ñ‚Ð²ÐµÑ€Ð¶Ð´ÐµÐ½Ð¸ÐµÐ¼ аккаунта." -#: mediagoblin/auth/views.py:260 +#: mediagoblin/auth/views.py:262 msgid "" "An email has been sent with instructions on how to change your password." msgstr "Вам отправлено Ñлектронное пиÑьмо Ñ Ð¸Ð½ÑтрукциÑми по Ñмене паролÑ." -#: mediagoblin/auth/views.py:270 +#: mediagoblin/auth/views.py:272 msgid "" "Could not send password recovery email as your username is inactive or your " "account's email address has not been verified." -msgstr "" -"Мы не можем отправить Ñообщение Ð´Ð»Ñ Ð²Ð¾ÑÑÑ‚Ð°Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ñ Ð¿Ð°Ñ€Ð¾Ð»Ñ, потому что ваша " -"ÑƒÑ‡Ñ‘Ñ‚Ð½Ð°Ñ Ð·Ð°Ð¿Ð¸ÑÑŒ неактивна, либо указанный в ней Ð°Ð´Ñ€ÐµÑ Ñлектронной почты не " -"был подтверждён." +msgstr "Мы не можем отправить Ñообщение Ð´Ð»Ñ Ð²Ð¾ÑÑÑ‚Ð°Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ñ Ð¿Ð°Ñ€Ð¾Ð»Ñ, потому что ваша ÑƒÑ‡Ñ‘Ñ‚Ð½Ð°Ñ Ð·Ð°Ð¿Ð¸ÑÑŒ неактивна, либо указанный в ней Ð°Ð´Ñ€ÐµÑ Ñлектронной почты не был подтверждён." -#: mediagoblin/auth/views.py:282 +#: mediagoblin/auth/views.py:284 msgid "Couldn't find someone with that username or email." -msgstr "" -"Ðе найдено никого Ñ Ñ‚Ð°ÐºÐ¸Ð¼ именем Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ Ð¸Ð»Ð¸ адреÑом Ñлектронной почты." +msgstr "Ðе найдено никого Ñ Ñ‚Ð°ÐºÐ¸Ð¼ именем Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ Ð¸Ð»Ð¸ адреÑом Ñлектронной почты." -#: mediagoblin/auth/views.py:330 +#: mediagoblin/auth/views.py:332 msgid "You can now log in using your new password." msgstr "Теперь вы можете войти, иÑÐ¿Ð¾Ð»ÑŒÐ·ÑƒÑ Ð²Ð°Ñˆ новый пароль." @@ -110,10 +102,7 @@ msgid "" "You can use\n" " <a href=\"http://daringfireball.net/projects/markdown/basics\">\n" " Markdown</a> for formatting." -msgstr "" -"Ð”Ð»Ñ Ñ€Ð°Ð·Ð¼ÐµÑ‚ÐºÐ¸ можете иÑпользовать Ñзык\n" -" <a href=\"http://daringfireball.net/projects/markdown/basics\">\n" -" Markdown</a>." +msgstr "Ð”Ð»Ñ Ñ€Ð°Ð·Ð¼ÐµÑ‚ÐºÐ¸ можете иÑпользовать Ñзык\n <a href=\"http://daringfireball.net/projects/markdown/basics\">\n Markdown</a>." #: mediagoblin/edit/forms.py:33 mediagoblin/submit/forms.py:36 msgid "Tags" @@ -135,11 +124,10 @@ msgstr "ÐžÑ‚Ð»Ð¸Ñ‡Ð¸Ñ‚ÐµÐ»ÑŒÐ½Ð°Ñ Ñ‡Ð°Ñть адреÑа необходима" msgid "" "The title part of this media's address. You usually don't need to change " "this." -msgstr "" -"ЧаÑть адреÑа Ñтого файла, Ð¿Ñ€Ð¾Ð¸Ð·Ð²Ð¾Ð´Ð½Ð°Ñ Ð¾Ñ‚ его названиÑ. Её обычно не " -"требуетÑÑ Ð¸Ð·Ð¼ÐµÐ½Ñть." +msgstr "ЧаÑть адреÑа Ñтого файла, Ð¿Ñ€Ð¾Ð¸Ð·Ð²Ð¾Ð´Ð½Ð°Ñ Ð¾Ñ‚ его названиÑ. Её обычно не требуетÑÑ Ð¸Ð·Ð¼ÐµÐ½Ñть." #: mediagoblin/edit/forms.py:44 mediagoblin/submit/forms.py:41 +#: mediagoblin/templates/mediagoblin/utils/license.html:20 msgid "License" msgstr "ЛицензиÑ" @@ -157,36 +145,33 @@ msgstr "Старый пароль" #: mediagoblin/edit/forms.py:65 msgid "Enter your old password to prove you own this account." -msgstr "" -"Введите Ñвой Ñтарый пароль в качеÑтве доказательÑтва, что Ñто ваша ÑƒÑ‡Ñ‘Ñ‚Ð½Ð°Ñ " -"запиÑÑŒ." +msgstr "Введите Ñвой Ñтарый пароль в качеÑтве доказательÑтва, что Ñто ваша ÑƒÑ‡Ñ‘Ñ‚Ð½Ð°Ñ Ð·Ð°Ð¿Ð¸ÑÑŒ." #: mediagoblin/edit/forms.py:68 msgid "New password" msgstr "Ðовый пароль" -#: mediagoblin/edit/views.py:68 +#: mediagoblin/edit/views.py:67 msgid "An entry with that slug already exists for this user." -msgstr "" -"У Ñтого Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ ÑƒÐ¶Ðµ еÑть файл Ñ Ñ‚Ð°ÐºÐ¾Ð¹ отличительной чаÑтью адреÑа." +msgstr "У Ñтого Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ ÑƒÐ¶Ðµ еÑть файл Ñ Ñ‚Ð°ÐºÐ¾Ð¹ отличительной чаÑтью адреÑа." -#: mediagoblin/edit/views.py:92 +#: mediagoblin/edit/views.py:88 msgid "You are editing another user's media. Proceed with caution." msgstr "Ð’Ñ‹ редактируете файлы другого пользователÑ. Будьте оÑторожны." -#: mediagoblin/edit/views.py:162 +#: mediagoblin/edit/views.py:158 msgid "You are editing a user's profile. Proceed with caution." msgstr "Ð’Ñ‹ редактируете профиль пользователÑ. Будьте оÑторожны." -#: mediagoblin/edit/views.py:180 +#: mediagoblin/edit/views.py:174 msgid "Profile changes saved" msgstr "Ð˜Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð¿Ñ€Ð¾Ñ„Ð¸Ð»Ñ Ñохранены" -#: mediagoblin/edit/views.py:206 +#: mediagoblin/edit/views.py:200 msgid "Wrong password" msgstr "Ðеправильный пароль" -#: mediagoblin/edit/views.py:222 +#: mediagoblin/edit/views.py:216 msgid "Account settings saved" msgstr "ÐаÑтройки учётной запиÑи запиÑаны" @@ -206,7 +191,7 @@ msgstr "Файл" msgid "You must provide a file." msgstr "Ð’Ñ‹ должны загрузить файл." -#: mediagoblin/submit/views.py:158 +#: mediagoblin/submit/views.py:156 msgid "Woohoo! Submitted!" msgstr "Ура! Файл загружен!" @@ -254,10 +239,16 @@ msgstr "Войти" #: mediagoblin/templates/mediagoblin/base.html:84 msgid "" "Powered by <a href=\"http://mediagoblin.org\">MediaGoblin</a>, a <a " -"href=\"http://gnu.org/\">GNU</a> project" +"href=\"http://gnu.org/\">GNU</a> project." +msgstr "" + +#: mediagoblin/templates/mediagoblin/base.html:87 +#, python-format +msgid "" +"Released under the <a " +"href=\"http://www.fsf.org/licensing/licenses/agpl-3.0.html\">AGPL</a>. <a " +"href=\"%(source_link)s\">Source code</a> available." msgstr "" -"Работает на <a href=\"http://mediagoblin.org\">MediaGoblin</a>, проекте <a " -"href=\"http://gnu.org/\">GNU</a>" #: mediagoblin/templates/mediagoblin/root.html:24 msgid "Explore" @@ -271,18 +262,13 @@ msgstr "Привет! Добро пожаловать на наш MediaGoblin’ msgid "" "This site is running <a href=\"http://mediagoblin.org\">MediaGoblin</a>, an " "extraordinarily great piece of media hosting software." -msgstr "" -"Ðтот Ñайт работает на <a href=\"http://mediagoblin.org\">MediaGoblin</a>, " -"необыкновенно замечательном ПО Ð´Ð»Ñ Ñ…Ð¾Ñтинга мультимедийных файлов." +msgstr "Ðтот Ñайт работает на <a href=\"http://mediagoblin.org\">MediaGoblin</a>, необыкновенно замечательном ПО Ð´Ð»Ñ Ñ…Ð¾Ñтинга мультимедийных файлов." #: mediagoblin/templates/mediagoblin/root.html:29 msgid "" "To add your own media, place comments, save your favourites and more, you " "can log in with your MediaGoblin account." -msgstr "" -"Ð”Ð»Ñ Ð´Ð¾Ð±Ð°Ð²Ð»ÐµÐ½Ð¸Ñ ÑобÑтвенных файлов, комментированиÑ, Ð²ÐµÐ´ÐµÐ½Ð¸Ñ ÑпиÑка любимых " -"файлов и Ñ‚. п. вы можете предÑтавитьÑÑ Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ вашей MediaGoblin’овой " -"учётной запиÑи." +msgstr "Ð”Ð»Ñ Ð´Ð¾Ð±Ð°Ð²Ð»ÐµÐ½Ð¸Ñ ÑобÑтвенных файлов, комментированиÑ, Ð²ÐµÐ´ÐµÐ½Ð¸Ñ ÑпиÑка любимых файлов и Ñ‚. п. вы можете предÑтавитьÑÑ Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ вашей MediaGoblin’овой учётной запиÑи." #: mediagoblin/templates/mediagoblin/root.html:31 msgid "Don't have one yet? It's easy!" @@ -294,10 +280,7 @@ msgid "" "<a class=\"button_action_highlight\" href=\"%(register_url)s\">Create an account at this site</a>\n" " or\n" " <a class=\"button_action\" href=\"http://wiki.mediagoblin.org/HackingHowto\">Set up MediaGoblin on your own server</a>" -msgstr "" -"<a class=\"button_action_highlight\" href=\"%(register_url)s\">Создайте учётную запиÑÑŒ на Ñтом Ñайте</a>\n" -" или\n" -" <a class=\"button_action\" href=\"http://wiki.mediagoblin.org/HackingHowto\">уÑтановите MediaGoblin на ÑобÑтвенный Ñервер</a>" +msgstr "<a class=\"button_action_highlight\" href=\"%(register_url)s\">Создайте учётную запиÑÑŒ на Ñтом Ñайте</a>\n или\n <a class=\"button_action\" href=\"http://wiki.mediagoblin.org/HackingHowto\">уÑтановите MediaGoblin на ÑобÑтвенный Ñервер</a>" #: mediagoblin/templates/mediagoblin/root.html:40 msgid "Most recent media" @@ -331,16 +314,7 @@ msgid "" "\n" "If you think this is an error, just ignore this email and continue being\n" "a happy goblin!" -msgstr "" -"Привет, %(username)s,\n" -"\n" -"чтобы Ñменить Ñвой пароль от GNU MediaGoblin, откройте\n" -"Ñледующий URL вашим вебâ€Ð±Ñ€Ð°ÑƒÐ·ÐµÑ€Ð¾Ð¼:\n" -"\n" -"%(verification_url)s\n" -"\n" -"ЕÑли вы думаете, что Ñто какаÑâ€Ñ‚о ошибка, то игнорируйте\n" -"Ñто Ñообщение и продолжайте быть ÑчаÑтливым гоблином!" +msgstr "Привет, %(username)s,\n\nчтобы Ñменить Ñвой пароль от GNU MediaGoblin, откройте\nÑледующий URL вашим вебâ€Ð±Ñ€Ð°ÑƒÐ·ÐµÑ€Ð¾Ð¼:\n\n%(verification_url)s\n\nЕÑли вы думаете, что Ñто какаÑâ€Ñ‚о ошибка, то игнорируйте\nÑто Ñообщение и продолжайте быть ÑчаÑтливым гоблином!" #: mediagoblin/templates/mediagoblin/auth/login.html:30 msgid "Logging in failed!" @@ -375,12 +349,7 @@ msgid "" "your web browser:\n" "\n" "%(verification_url)s" -msgstr "" -"Привет, %(username)s!\n" -"\n" -"Чтобы активировать Ñвой аккаунт в GNU MediaGoblin, откройте в Ñвоём вебâ€Ð±Ñ€Ð°ÑƒÐ·ÐµÑ€Ðµ Ñледующую ÑÑылку:\n" -"\n" -"%(verification_url)s" +msgstr "Привет, %(username)s!\n\nЧтобы активировать Ñвой аккаунт в GNU MediaGoblin, откройте в Ñвоём вебâ€Ð±Ñ€Ð°ÑƒÐ·ÐµÑ€Ðµ Ñледующую ÑÑылку:\n\n%(verification_url)s" #: mediagoblin/templates/mediagoblin/edit/edit.html:29 #, python-format @@ -415,29 +384,23 @@ msgid "Media tagged with: %(tag_name)s" msgstr "Файлы Ñ Ð¼ÐµÑ‚ÐºÐ¾Ð¹: %(tag_name)s" #: mediagoblin/templates/mediagoblin/media_displays/ascii.html:34 -#: mediagoblin/templates/mediagoblin/media_displays/video.html:46 +#: mediagoblin/templates/mediagoblin/media_displays/video.html:53 msgid "Original" msgstr "Оригинал" -#: mediagoblin/templates/mediagoblin/media_displays/video.html:33 +#: mediagoblin/templates/mediagoblin/media_displays/video.html:40 msgid "" "Sorry, this video will not work because \n" "\t your web browser does not support HTML5 \n" "\t video." -msgstr "" -"Сожалеем, Ñтот ролик не проиграетÑÑ, âŽ\n" -"» потому что ваш браузер не поддерживает âŽ\n" -"» видео в ÑоответÑтвии Ñо Ñтандартом HTML5." +msgstr "Сожалеем, Ñтот ролик не проиграетÑÑ, âŽ\n» потому что ваш браузер не поддерживает âŽ\n» видео в ÑоответÑтвии Ñо Ñтандартом HTML5." -#: mediagoblin/templates/mediagoblin/media_displays/video.html:36 +#: mediagoblin/templates/mediagoblin/media_displays/video.html:43 msgid "" "You can get a modern web browser that \n" "\t can play this video at <a href=\"http://getfirefox.com\">\n" "\t http://getfirefox.com</a>!" -msgstr "" -"Ð’Ñ‹ можете Ñкачать Ñовременный браузер,\n" -"<span class=\"whitespace other\" title=\"Tab\">»</span> ÑпоÑобный воÑпроизводить Ñто видео, Ñ <a href=\"http://getfirefox.com\">\n" -"<span class=\"whitespace other\" title=\"Tab\">»</span> http://getfirefox.com</a>!" +msgstr "Ð’Ñ‹ можете Ñкачать Ñовременный браузер,\n<span class=\"whitespace other\" title=\"Tab\">»</span> ÑпоÑобный воÑпроизводить Ñто видео, Ñ <a href=\"http://getfirefox.com\">\n<span class=\"whitespace other\" title=\"Tab\">»</span> http://getfirefox.com</a>!" #: mediagoblin/templates/mediagoblin/submit/start.html:26 msgid "Add your media" @@ -457,60 +420,44 @@ msgstr "Файлы %(username)s" msgid "<a href=\"%(user_url)s\">%(username)s</a>'s media" msgstr "Файлы Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ <a href=\"%(user_url)s\">%(username)s</a>" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:72 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:46 #, python-format -msgid "Added on %(date)s." -msgstr "Добавлено %(date)s." +msgid "â– Browsing media by <a href=\"%(user_url)s\">%(username)s</a>" +msgstr "" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:81 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:85 msgid "Edit" msgstr "Изменить" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:85 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:89 msgid "Delete" msgstr "Удалить" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:91 -#, python-format -msgid "%(comment_count)s comment" -msgstr "%(comment_count)s комментарий" - -#: mediagoblin/templates/mediagoblin/user_pages/media.html:93 -#, python-format -msgid "%(comment_count)s comments" -msgstr "%(comment_count)s комментариев" - -#: mediagoblin/templates/mediagoblin/user_pages/media.html:95 -msgid "No comments yet." -msgstr "Комментариев пока нет." - -#: mediagoblin/templates/mediagoblin/user_pages/media.html:103 -msgid "Add one" -msgstr "Комментировать" +#: mediagoblin/templates/mediagoblin/user_pages/media.html:124 +msgid "Add a comment" +msgstr "" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:112 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:133 msgid "" "You can use <a " "href=\"http://daringfireball.net/projects/markdown/basics\">Markdown</a> for" " formatting." -msgstr "" -"Ð”Ð»Ñ Ñ€Ð°Ð·Ð¼ÐµÑ‚ÐºÐ¸ можете иÑпользовать Ñзык <a " -"href=\"http://daringfireball.net/projects/markdown/basics\">Markdown</a>." +msgstr "Ð”Ð»Ñ Ñ€Ð°Ð·Ð¼ÐµÑ‚ÐºÐ¸ можете иÑпользовать Ñзык <a href=\"http://daringfireball.net/projects/markdown/basics\">Markdown</a>." -#: mediagoblin/templates/mediagoblin/user_pages/media.html:116 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:137 msgid "Add this comment" msgstr "Добавить Ñтот комментарий" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:138 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:159 msgid "at" msgstr "в" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:153 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:174 #, python-format -msgid "<p>â– Browsing media by <a href=\"%(user_url)s\">%(username)s</a></p>" +msgid "" +"<h3>Added on</h3>\n" +" <p>%(date)s</p>" msgstr "" -"<p>■ПроÑмотр файлов Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ <a " -"href=\"%(user_url)s\">%(username)s</a></p>" #: mediagoblin/templates/mediagoblin/user_pages/media_confirm_delete.html:30 #, python-format @@ -518,8 +465,8 @@ msgid "Really delete %(title)s?" msgstr "ДейÑтвительно удалить %(title)s?" #: mediagoblin/templates/mediagoblin/user_pages/media_confirm_delete.html:50 -msgid "Delete Permanently" -msgstr "Удалить безвозвратно" +msgid "Delete permanently" +msgstr "" #: mediagoblin/templates/mediagoblin/user_pages/processing_panel.html:22 msgid "Media processing panel" @@ -528,8 +475,7 @@ msgstr "Панель обработки файлов" #: mediagoblin/templates/mediagoblin/user_pages/processing_panel.html:25 msgid "" "You can track the state of media being processed for your gallery here." -msgstr "" -"Ð’Ñ‹ можете Ñледить за ÑтатуÑом обработки файлов Ð´Ð»Ñ Ð²Ð°ÑˆÐµÐ¹ галереи здеÑÑŒ." +msgstr "Ð’Ñ‹ можете Ñледить за ÑтатуÑом обработки файлов Ð´Ð»Ñ Ð²Ð°ÑˆÐµÐ¹ галереи здеÑÑŒ." #: mediagoblin/templates/mediagoblin/user_pages/processing_panel.html:28 msgid "Media in-processing" @@ -565,9 +511,7 @@ msgstr "Почти закончили! Теперь надо активировР#: mediagoblin/templates/mediagoblin/user_pages/user.html:58 msgid "" "An email should arrive in a few moments with instructions on how to do so." -msgstr "" -"Через пару мгновений на Ð°Ð´Ñ€ÐµÑ Ð²Ð°ÑˆÐµÐ¹ Ñлектронной почты должно прийти " -"Ñообщение Ñ Ð´Ð°Ð»ÑŒÐ½ÐµÐ¹ÑˆÐ¸Ð¼Ð¸ инÑтрукциÑми." +msgstr "Через пару мгновений на Ð°Ð´Ñ€ÐµÑ Ð²Ð°ÑˆÐµÐ¹ Ñлектронной почты должно прийти Ñообщение Ñ Ð´Ð°Ð»ÑŒÐ½ÐµÐ¹ÑˆÐ¸Ð¼Ð¸ инÑтрукциÑми." #: mediagoblin/templates/mediagoblin/user_pages/user.html:62 msgid "In case it doesn't:" @@ -575,8 +519,7 @@ msgstr "РеÑли нет, то:" #: mediagoblin/templates/mediagoblin/user_pages/user.html:65 msgid "Resend verification email" -msgstr "" -"Повторно отправить Ñообщение Ð´Ð»Ñ Ð¿Ð¾Ð´Ð²ÐµÑ€Ð¶Ð´ÐµÐ½Ð¸Ñ Ð°Ð´Ñ€ÐµÑа Ñлектронной почты" +msgstr "Повторно отправить Ñообщение Ð´Ð»Ñ Ð¿Ð¾Ð´Ð²ÐµÑ€Ð¶Ð´ÐµÐ½Ð¸Ñ Ð°Ð´Ñ€ÐµÑа Ñлектронной почты" #: mediagoblin/templates/mediagoblin/user_pages/user.html:73 msgid "" @@ -589,9 +532,7 @@ msgstr "Ктоâ€Ñ‚о Ñоздал аккаунт Ñ Ñтим именем, но msgid "" "If you are that person but you've lost your verification email, you can <a " "href=\"%(login_url)s\">log in</a> and resend it." -msgstr "" -"ЕÑли Ñто были вы, и еÑли вы потерÑли Ñообщение Ð´Ð»Ñ Ð¿Ð¾Ð´Ñ‚Ð²ÐµÑ€Ð¶Ð´ÐµÐ½Ð¸Ñ Ð°ÐºÐºÐ°ÑƒÐ½Ñ‚Ð°, " -"то вы можете <a href=\"%(login_url)s\">войти</a> и отправить его повторно." +msgstr "ЕÑли Ñто были вы, и еÑли вы потерÑли Ñообщение Ð´Ð»Ñ Ð¿Ð¾Ð´Ñ‚Ð²ÐµÑ€Ð¶Ð´ÐµÐ½Ð¸Ñ Ð°ÐºÐºÐ°ÑƒÐ½Ñ‚Ð°, то вы можете <a href=\"%(login_url)s\">войти</a> и отправить его повторно." #: mediagoblin/templates/mediagoblin/user_pages/user.html:96 msgid "Here's a spot to tell others about yourself." @@ -634,9 +575,14 @@ msgstr "значок ленты" msgid "Atom feed" msgstr "лента в формате Atom" -#: mediagoblin/templates/mediagoblin/utils/license.html:21 -msgid "License:" -msgstr "ЛицензиÑ:" +#: mediagoblin/templates/mediagoblin/utils/geolocation_map.html:25 +msgid "Location" +msgstr "" + +#: mediagoblin/templates/mediagoblin/utils/geolocation_map.html:38 +#, python-format +msgid "View on <a href=\"%(osm_url)s\">OpenStreetMap</a>" +msgstr "" #: mediagoblin/templates/mediagoblin/utils/license.html:25 msgid "All rights reserved" @@ -654,23 +600,19 @@ msgstr "Более Ñтарые →" msgid "Go to page:" msgstr "Перейти к Ñтранице:" -#: mediagoblin/templates/mediagoblin/utils/prev_next.html:27 -#: mediagoblin/templates/mediagoblin/utils/prev_next.html:32 +#: mediagoblin/templates/mediagoblin/utils/prev_next.html:28 +#: mediagoblin/templates/mediagoblin/utils/prev_next.html:33 msgid "newer" msgstr "более новые" -#: mediagoblin/templates/mediagoblin/utils/prev_next.html:38 -#: mediagoblin/templates/mediagoblin/utils/prev_next.html:43 +#: mediagoblin/templates/mediagoblin/utils/prev_next.html:39 +#: mediagoblin/templates/mediagoblin/utils/prev_next.html:44 msgid "older" msgstr "более Ñтарые" #: mediagoblin/templates/mediagoblin/utils/tags.html:20 -msgid "View more media tagged with" -msgstr "Ðайти другие файлы Ñ Ð¼ÐµÑ‚ÐºÐ¾Ð¹" - -#: mediagoblin/templates/mediagoblin/utils/tags.html:25 -msgid "or" -msgstr "или" +msgid "Tagged with" +msgstr "" #: mediagoblin/tools/exif.py:68 msgid "Could not read the image file." @@ -680,24 +622,22 @@ msgstr "Ðе удалоÑÑŒ прочитать файл Ñ Ð¸Ð·Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸Ð msgid "I am sure I want to delete this" msgstr "Я уверен, что хочу удалить Ñто" -#: mediagoblin/user_pages/views.py:155 +#: mediagoblin/user_pages/views.py:153 msgid "Oops, your comment was empty." msgstr "Ой, ваш комментарий был пуÑÑ‚." -#: mediagoblin/user_pages/views.py:161 +#: mediagoblin/user_pages/views.py:159 msgid "Your comment has been posted!" msgstr "Ваш комментарий размещён!" -#: mediagoblin/user_pages/views.py:183 +#: mediagoblin/user_pages/views.py:185 msgid "You deleted the media." msgstr "Ð’Ñ‹ удалили файл." -#: mediagoblin/user_pages/views.py:190 +#: mediagoblin/user_pages/views.py:192 msgid "The media was not deleted because you didn't check that you were sure." msgstr "Файл не удалён, так как вы не подтвердили Ñвою уверенноÑть галочкой." -#: mediagoblin/user_pages/views.py:198 +#: mediagoblin/user_pages/views.py:200 msgid "You are about to delete another user's media. Proceed with caution." msgstr "Ð’Ñ‹ на пороге ÑƒÐ´Ð°Ð»ÐµÐ½Ð¸Ñ Ñ„Ð°Ð¹Ð»Ð° другого пользователÑ. Будьте оÑторожны." - - diff --git a/mediagoblin/i18n/sk/LC_MESSAGES/mediagoblin.mo b/mediagoblin/i18n/sk/LC_MESSAGES/mediagoblin.mo Binary files differindex e79b6848..7d897bf9 100644 --- a/mediagoblin/i18n/sk/LC_MESSAGES/mediagoblin.mo +++ b/mediagoblin/i18n/sk/LC_MESSAGES/mediagoblin.mo diff --git a/mediagoblin/i18n/sk/LC_MESSAGES/mediagoblin.po b/mediagoblin/i18n/sk/LC_MESSAGES/mediagoblin.po index 8cde8800..6b9e38b9 100644 --- a/mediagoblin/i18n/sk/LC_MESSAGES/mediagoblin.po +++ b/mediagoblin/i18n/sk/LC_MESSAGES/mediagoblin.po @@ -3,13 +3,14 @@ # This file is distributed under the same license as the PROJECT project. # # Translators: +# Martin Zatroch <zatroch.martin@gmail.com>, 2012. # <zatroch.martin@gmail.com>, 2011, 2012. msgid "" msgstr "" "Project-Id-Version: GNU MediaGoblin\n" -"Report-Msgid-Bugs-To: http://bugs.foocorp.net/projects/mediagoblin/issues\n" -"POT-Creation-Date: 2012-01-29 13:31-0600\n" -"PO-Revision-Date: 2012-01-29 19:29+0000\n" +"Report-Msgid-Bugs-To: http://issues.mediagoblin.org/\n" +"POT-Creation-Date: 2012-03-18 15:16-0500\n" +"PO-Revision-Date: 2012-03-18 20:14+0000\n" "Last-Translator: cwebber <cwebber@dustycloud.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n" "MIME-Version: 1.0\n" @@ -19,7 +20,7 @@ msgstr "" "Language: sk\n" "Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2\n" -#: mediagoblin/processing.py:143 +#: mediagoblin/processing.py:154 msgid "Invalid file given for media type." msgstr "Odovzdaný nesprávny súbor pre daný typ média." @@ -39,60 +40,52 @@ msgstr "E-mailová adresa" msgid "Sorry, registration is disabled on this instance." msgstr "PrepáÄ, registrácia na tejto inÅ¡tancii nie je povolená." -#: mediagoblin/auth/views.py:73 +#: mediagoblin/auth/views.py:75 msgid "Sorry, a user with that name already exists." msgstr "PrepáÄ, rovnaké prihlasovacie meno už niekto použÃva." -#: mediagoblin/auth/views.py:77 +#: mediagoblin/auth/views.py:79 msgid "Sorry, a user with that email address already exists." msgstr "PrepáÄ, použÃvateľ s rovnakou e-mailovou adresou už existuje." -#: mediagoblin/auth/views.py:180 +#: mediagoblin/auth/views.py:182 msgid "" "Your email address has been verified. You may now login, edit your profile, " "and submit images!" -msgstr "" -"Tvoja e-mailová adresa bola úspeÅ¡ne overená. MôžeÅ¡ sa hneÄ prihlásiÅ¥, " -"upraviÅ¥ svoj profil a vkladaÅ¥ výtvory! " +msgstr "Tvoja e-mailová adresa bola úspeÅ¡ne overená. MôžeÅ¡ sa hneÄ prihlásiÅ¥, upraviÅ¥ svoj profil a vkladaÅ¥ výtvory! " -#: mediagoblin/auth/views.py:186 +#: mediagoblin/auth/views.py:188 msgid "The verification key or user id is incorrect" msgstr "Nesprávny overovacà kÄ¾ÃºÄ alebo použÃvateľský identifikátor" -#: mediagoblin/auth/views.py:204 +#: mediagoblin/auth/views.py:206 msgid "You must be logged in so we know who to send the email to!" -msgstr "" -"Aby sme ti mohli zaslaÅ¥ e-mailovú správu, je potrebné byÅ¥ prihláseným!" +msgstr "Aby sme ti mohli zaslaÅ¥ e-mailovú správu, je potrebné byÅ¥ prihláseným!" -#: mediagoblin/auth/views.py:212 +#: mediagoblin/auth/views.py:214 msgid "You've already verified your email address!" msgstr "Tvoja e-mailová adresa už bola raz overená!" -#: mediagoblin/auth/views.py:225 +#: mediagoblin/auth/views.py:227 msgid "Resent your verification email." msgstr "Opätovne zaslaÅ¥ overovaciu správu na e-mail." -#: mediagoblin/auth/views.py:260 +#: mediagoblin/auth/views.py:262 msgid "" "An email has been sent with instructions on how to change your password." msgstr "E-mailová správa s inÅ¡trukciami pre zmenu tvojho hesla bola odoslaná." -#: mediagoblin/auth/views.py:270 +#: mediagoblin/auth/views.py:272 msgid "" "Could not send password recovery email as your username is inactive or your " "account's email address has not been verified." -msgstr "" -"Nebolo ti možné zaslaÅ¥ e-mailovú správu ohľadom obnovy hesla, nakoľko je " -"tvoje použÃvateľské meno buÄ neaktÃvne alebo e-mailová adresa úÄtu " -"neoverená." +msgstr "Nebolo ti možné zaslaÅ¥ e-mailovú správu ohľadom obnovy hesla, nakoľko je tvoje použÃvateľské meno buÄ neaktÃvne alebo e-mailová adresa úÄtu neoverená." -#: mediagoblin/auth/views.py:282 +#: mediagoblin/auth/views.py:284 msgid "Couldn't find someone with that username or email." -msgstr "" -"Nebolo možné nájsÅ¥ nikoho s týmto použÃvateľským menom alebo e-mailovou " -"adresou." +msgstr "Nebolo možné nájsÅ¥ nikoho s týmto použÃvateľským menom alebo e-mailovou adresou." -#: mediagoblin/auth/views.py:330 +#: mediagoblin/auth/views.py:332 msgid "You can now log in using your new password." msgstr "Teraz sa môžeÅ¡ prihlásiÅ¥ so svojim novým heslom." @@ -110,10 +103,7 @@ msgid "" "You can use\n" " <a href=\"http://daringfireball.net/projects/markdown/basics\">\n" " Markdown</a> for formatting." -msgstr "" -"MôžeÅ¡ využiÅ¥\n" -" <a href=\"http://daringfireball.net/projects/markdown/basics\">\n" -" Markdown</a> pri formátovanÃ." +msgstr "MôžeÅ¡ využiÅ¥\n <a href=\"http://daringfireball.net/projects/markdown/basics\">\n Markdown</a> pri formátovanÃ." #: mediagoblin/edit/forms.py:33 mediagoblin/submit/forms.py:36 msgid "Tags" @@ -138,8 +128,9 @@ msgid "" msgstr "Titulná ÄasÅ¥ adresy tohto výtvoru. VäÄÅ¡inou nie je potrebná zmena." #: mediagoblin/edit/forms.py:44 mediagoblin/submit/forms.py:41 +#: mediagoblin/templates/mediagoblin/utils/license.html:20 msgid "License" -msgstr "" +msgstr "Licencia" #: mediagoblin/edit/forms.py:50 msgid "Bio" @@ -161,27 +152,27 @@ msgstr "PotvrÄ, že vlastnÃÅ¡ tento úÄet zadanÃm svojho starého hesla." msgid "New password" msgstr "Nové heslo" -#: mediagoblin/edit/views.py:68 +#: mediagoblin/edit/views.py:67 msgid "An entry with that slug already exists for this user." msgstr "Položku s rovnakou unikátnou ÄasÅ¥ou adresy už niekde máš." -#: mediagoblin/edit/views.py:92 +#: mediagoblin/edit/views.py:88 msgid "You are editing another user's media. Proceed with caution." msgstr "UpravujeÅ¡ médiá niekoho iného. Dbaj na to." -#: mediagoblin/edit/views.py:162 +#: mediagoblin/edit/views.py:158 msgid "You are editing a user's profile. Proceed with caution." msgstr "UpravujeÅ¡ použÃvateľský profil. Dbaj na to." -#: mediagoblin/edit/views.py:180 +#: mediagoblin/edit/views.py:174 msgid "Profile changes saved" msgstr "Úpravy profilu uložené" -#: mediagoblin/edit/views.py:206 +#: mediagoblin/edit/views.py:200 msgid "Wrong password" msgstr "Nesprávne heslo" -#: mediagoblin/edit/views.py:222 +#: mediagoblin/edit/views.py:216 msgid "Account settings saved" msgstr "Nastavenia úÄtu uložené" @@ -201,7 +192,7 @@ msgstr "Súbor" msgid "You must provide a file." msgstr "MusÃÅ¡ poskytnúť súbor." -#: mediagoblin/submit/views.py:158 +#: mediagoblin/submit/views.py:156 msgid "Woohoo! Submitted!" msgstr "Juchú! ÚspeÅ¡ne vložené!" @@ -221,9 +212,7 @@ msgstr "Na danej adrese sa stránka zrejme nenachádza. PrepáÄ!" msgid "" "If you're sure the address is correct, maybe the page you're looking for has" " been moved or deleted." -msgstr "" -"Ak vieÅ¡ s istotou, že adresa je správna, tak najskôr bola hľadaná stánka " -"presunutá alebo zmazaná." +msgstr "Ak vieÅ¡ s istotou, že adresa je správna, tak najskôr bola hľadaná stánka presunutá alebo zmazaná." #: mediagoblin/templates/mediagoblin/base.html:46 msgid "MediaGoblin logo" @@ -251,10 +240,16 @@ msgstr "Prihlásenie" #: mediagoblin/templates/mediagoblin/base.html:84 msgid "" "Powered by <a href=\"http://mediagoblin.org\">MediaGoblin</a>, a <a " -"href=\"http://gnu.org/\">GNU</a> project" +"href=\"http://gnu.org/\">GNU</a> project." +msgstr "" + +#: mediagoblin/templates/mediagoblin/base.html:87 +#, python-format +msgid "" +"Released under the <a " +"href=\"http://www.fsf.org/licensing/licenses/agpl-3.0.html\">AGPL</a>. <a " +"href=\"%(source_link)s\">Source code</a> available." msgstr "" -"Poháňa nás <a href=\"http://mediagoblin.org\">MediaGoblin</a>, súÄasÅ¥ " -"projektu <a href=\"http://gnu.org/\">GNU</a>" #: mediagoblin/templates/mediagoblin/root.html:24 msgid "Explore" @@ -268,17 +263,13 @@ msgstr "Ahoj, vitaj na tejto MediaGoblin stránke!" msgid "" "This site is running <a href=\"http://mediagoblin.org\">MediaGoblin</a>, an " "extraordinarily great piece of media hosting software." -msgstr "" -"Táto stránka použÃva <a href=\"http://mediagoblin.org\">MediaGoblin</a>, " -"výnimoÄne skvelý kus softvéru na hostovanie médiÃ." +msgstr "Táto stránka použÃva <a href=\"http://mediagoblin.org\">MediaGoblin</a>, výnimoÄne skvelý kus softvéru na hostovanie médiÃ." #: mediagoblin/templates/mediagoblin/root.html:29 msgid "" "To add your own media, place comments, save your favourites and more, you " "can log in with your MediaGoblin account." -msgstr "" -"Pre pridanie vlastných výtvorov, vloženie komentárov, uloženie svojich " -"obľúbených položiek a viac, sa musÃÅ¡ prihlásiÅ¥ so svojim MediaGoblin úÄtom." +msgstr "Pre pridanie vlastných výtvorov, vloženie komentárov, uloženie svojich obľúbených položiek a viac, sa musÃÅ¡ prihlásiÅ¥ so svojim MediaGoblin úÄtom." #: mediagoblin/templates/mediagoblin/root.html:31 msgid "Don't have one yet? It's easy!" @@ -290,10 +281,7 @@ msgid "" "<a class=\"button_action_highlight\" href=\"%(register_url)s\">Create an account at this site</a>\n" " or\n" " <a class=\"button_action\" href=\"http://wiki.mediagoblin.org/HackingHowto\">Set up MediaGoblin on your own server</a>" -msgstr "" -"<a class=\"button_action_highlight\" href=\"%(register_url)s\">VytvoriÅ¥ úÄet na tejto stránke</a>\n" -" alebo\n" -" <a class=\"button_action\" href=\"http://wiki.mediagoblin.org/HackingHowto\">ZaložiÅ¥ MediaGoblin na vlastnom serveri</a>" +msgstr "<a class=\"button_action_highlight\" href=\"%(register_url)s\">VytvoriÅ¥ úÄet na tejto stránke</a>\n alebo\n <a class=\"button_action\" href=\"http://wiki.mediagoblin.org/HackingHowto\">ZaložiÅ¥ MediaGoblin na vlastnom serveri</a>" #: mediagoblin/templates/mediagoblin/root.html:40 msgid "Most recent media" @@ -327,14 +315,7 @@ msgid "" "\n" "If you think this is an error, just ignore this email and continue being\n" "a happy goblin!" -msgstr "" -"Ahoj %(username)s,\n" -"\n" -"pre zmenu svojho hesla k GNU MediaGoblin úÄtu, otvor nasledujúci odkaz vo svojom prehliadaÄi:\n" -"\n" -"%(verification_url)s\n" -"\n" -"Pokiaľ si myslÃÅ¡, že doÅ¡lo k omylu, tak jednoducho ignoruj túto správu a buÄ Å¡Å¥astným goblinom!" +msgstr "Ahoj %(username)s,\n\npre zmenu svojho hesla k GNU MediaGoblin úÄtu, otvor nasledujúci odkaz vo svojom prehliadaÄi:\n\n%(verification_url)s\n\nPokiaľ si myslÃÅ¡, že doÅ¡lo k omylu, tak jednoducho ignoruj túto správu a buÄ Å¡Å¥astným goblinom!" #: mediagoblin/templates/mediagoblin/auth/login.html:30 msgid "Logging in failed!" @@ -369,13 +350,7 @@ msgid "" "your web browser:\n" "\n" "%(verification_url)s" -msgstr "" -"Ahoj %(username)s,\n" -"\n" -"pre aktiváciu tvojho GNU MediaGoblin úÄtu, otvor nasledujúci odkaz vo\n" -"svojom prehliadaÄi:\n" -"\n" -"%(verification_url)s" +msgstr "Ahoj %(username)s,\n\npre aktiváciu tvojho GNU MediaGoblin úÄtu, otvor nasledujúci odkaz vo\nsvojom prehliadaÄi:\n\n%(verification_url)s" #: mediagoblin/templates/mediagoblin/edit/edit.html:29 #, python-format @@ -410,29 +385,23 @@ msgid "Media tagged with: %(tag_name)s" msgstr "Výtvory oznaÄené ako: %(tag_name)s" #: mediagoblin/templates/mediagoblin/media_displays/ascii.html:34 -#: mediagoblin/templates/mediagoblin/media_displays/video.html:46 +#: mediagoblin/templates/mediagoblin/media_displays/video.html:53 msgid "Original" msgstr "Originál" -#: mediagoblin/templates/mediagoblin/media_displays/video.html:33 +#: mediagoblin/templates/mediagoblin/media_displays/video.html:40 msgid "" "Sorry, this video will not work because \n" "\t your web browser does not support HTML5 \n" "\t video." -msgstr "" -"PrepáÄ, toto video nepôjde prehraÅ¥ \n" -"<span class=\"whitespace other\" title=\"Tab\">»</span> tvoj webový prehliadaÄ nepodporuje HTML5 \n" -"<span class=\"whitespace other\" title=\"Tab\">»</span> video." +msgstr "PrepáÄ, toto video nepôjde prehraÅ¥ \n<span class=\"whitespace other\" title=\"Tab\">»</span> tvoj webový prehliadaÄ nepodporuje HTML5 \n<span class=\"whitespace other\" title=\"Tab\">»</span> video." -#: mediagoblin/templates/mediagoblin/media_displays/video.html:36 +#: mediagoblin/templates/mediagoblin/media_displays/video.html:43 msgid "" "You can get a modern web browser that \n" "\t can play this video at <a href=\"http://getfirefox.com\">\n" "\t http://getfirefox.com</a>!" -msgstr "" -"MôžeÅ¡ zÃskaÅ¥ moderný prehliadaÄ, ktorý \n" -"<span class=\"whitespace other\" title=\"Tab\">»</span> vie prehraÅ¥ toto video na <a href=\"http://getfirefox.com\">\n" -"<span class=\"whitespace other\" title=\"Tab\">»</span> http://getfirefox.com</a>!" +msgstr "MôžeÅ¡ zÃskaÅ¥ moderný prehliadaÄ, ktorý \n<span class=\"whitespace other\" title=\"Tab\">»</span> vie prehraÅ¥ toto video na <a href=\"http://getfirefox.com\">\n<span class=\"whitespace other\" title=\"Tab\">»</span> http://getfirefox.com</a>!" #: mediagoblin/templates/mediagoblin/submit/start.html:26 msgid "Add your media" @@ -452,57 +421,44 @@ msgstr "Výtvory, ktoré vlastnà %(username)s" msgid "<a href=\"%(user_url)s\">%(username)s</a>'s media" msgstr "Výtvory, ktoré vlastnà <a href=\"%(user_url)s\">%(username)s</a>" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:72 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:46 #, python-format -msgid "Added on %(date)s." -msgstr "Pridané v %(date)s." +msgid "â– Browsing media by <a href=\"%(user_url)s\">%(username)s</a>" +msgstr "" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:81 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:85 msgid "Edit" msgstr "UpraviÅ¥" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:85 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:89 msgid "Delete" msgstr "OdstrániÅ¥" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:91 -#, python-format -msgid "%(comment_count)s comment" -msgstr "%(comment_count)s komentár" - -#: mediagoblin/templates/mediagoblin/user_pages/media.html:93 -#, python-format -msgid "%(comment_count)s comments" -msgstr "%(comment_count)s komentáre" - -#: mediagoblin/templates/mediagoblin/user_pages/media.html:95 -msgid "No comments yet." -msgstr "Zatiaľ žiadny komentár." - -#: mediagoblin/templates/mediagoblin/user_pages/media.html:103 -msgid "Add one" -msgstr "Pridaj jeden" +#: mediagoblin/templates/mediagoblin/user_pages/media.html:124 +msgid "Add a comment" +msgstr "" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:112 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:133 msgid "" "You can use <a " "href=\"http://daringfireball.net/projects/markdown/basics\">Markdown</a> for" " formatting." -msgstr "" +msgstr "MôžeÅ¡ využiÅ¥ <a href=\"http://daringfireball.net/projects/markdown/basics\">Markdown</a> pre formátovanie." -#: mediagoblin/templates/mediagoblin/user_pages/media.html:116 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:137 msgid "Add this comment" msgstr "PridaÅ¥ tento komentár" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:138 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:159 msgid "at" msgstr "o" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:153 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:174 #, python-format -msgid "<p>â– Browsing media by <a href=\"%(user_url)s\">%(username)s</a></p>" +msgid "" +"<h3>Added on</h3>\n" +" <p>%(date)s</p>" msgstr "" -"<p>â– Prezeranie výtvorov podľa <a href=\"%(user_url)s\">%(username)s</a></p>" #: mediagoblin/templates/mediagoblin/user_pages/media_confirm_delete.html:30 #, python-format @@ -510,8 +466,8 @@ msgid "Really delete %(title)s?" msgstr "SkutoÄne odstrániÅ¥ %(title)s?" #: mediagoblin/templates/mediagoblin/user_pages/media_confirm_delete.html:50 -msgid "Delete Permanently" -msgstr "OdstrániÅ¥ navždy" +msgid "Delete permanently" +msgstr "" #: mediagoblin/templates/mediagoblin/user_pages/processing_panel.html:22 msgid "Media processing panel" @@ -570,18 +526,14 @@ msgstr "Opätovne zaslaÅ¥ overovaciu správu na e-mail" msgid "" "Someone has registered an account with this username, but it still has to be" " activated." -msgstr "" -"ÚÄet s týmto prihlasovacÃm menom je už registrovaný, avÅ¡ak eÅ¡te stále " -"neaktÃvny." +msgstr "ÚÄet s týmto prihlasovacÃm menom je už registrovaný, avÅ¡ak eÅ¡te stále neaktÃvny." #: mediagoblin/templates/mediagoblin/user_pages/user.html:79 #, python-format msgid "" "If you are that person but you've lost your verification email, you can <a " "href=\"%(login_url)s\">log in</a> and resend it." -msgstr "" -"Pokiaľ si to ty, ale už nemáš overovaciu e-mailovú správu, tak sa môžeÅ¡ <a " -"href=\"%(login_url)s\">prihlásiÅ¥</a> a preposlaÅ¥ si ju." +msgstr "Pokiaľ si to ty, ale už nemáš overovaciu e-mailovú správu, tak sa môžeÅ¡ <a href=\"%(login_url)s\">prihlásiÅ¥</a> a preposlaÅ¥ si ju." #: mediagoblin/templates/mediagoblin/user_pages/user.html:96 msgid "Here's a spot to tell others about yourself." @@ -609,8 +561,7 @@ msgstr "ZhliadnuÅ¥ vÅ¡etky výtvory, ktoré vlastnà %(username)s" msgid "" "This is where your media will appear, but you don't seem to have added " "anything yet." -msgstr "" -"VÅ¡etky tvoje výtvory sa objavia práve tu, ale zatiaľ nemáš niÄ pridané." +msgstr "VÅ¡etky tvoje výtvory sa objavia práve tu, ale zatiaľ nemáš niÄ pridané." #: mediagoblin/templates/mediagoblin/user_pages/user.html:163 #: mediagoblin/templates/mediagoblin/utils/object_gallery.html:72 @@ -625,13 +576,18 @@ msgstr "ikona ÄÃtaÄky" msgid "Atom feed" msgstr "ÄŒÃtaÄka Atom" -#: mediagoblin/templates/mediagoblin/utils/license.html:21 -msgid "License:" +#: mediagoblin/templates/mediagoblin/utils/geolocation_map.html:25 +msgid "Location" +msgstr "" + +#: mediagoblin/templates/mediagoblin/utils/geolocation_map.html:38 +#, python-format +msgid "View on <a href=\"%(osm_url)s\">OpenStreetMap</a>" msgstr "" #: mediagoblin/templates/mediagoblin/utils/license.html:25 msgid "All rights reserved" -msgstr "" +msgstr "VÅ¡etky práva vyhradené" #: mediagoblin/templates/mediagoblin/utils/pagination.html:39 msgid "↠Newer" @@ -645,50 +601,44 @@ msgstr "StarÅ¡ie →" msgid "Go to page:" msgstr "PrejsÅ¥ na stránku:" -#: mediagoblin/templates/mediagoblin/utils/prev_next.html:27 -#: mediagoblin/templates/mediagoblin/utils/prev_next.html:32 +#: mediagoblin/templates/mediagoblin/utils/prev_next.html:28 +#: mediagoblin/templates/mediagoblin/utils/prev_next.html:33 msgid "newer" msgstr "novÅ¡ie" -#: mediagoblin/templates/mediagoblin/utils/prev_next.html:38 -#: mediagoblin/templates/mediagoblin/utils/prev_next.html:43 +#: mediagoblin/templates/mediagoblin/utils/prev_next.html:39 +#: mediagoblin/templates/mediagoblin/utils/prev_next.html:44 msgid "older" msgstr "starÅ¡ie" #: mediagoblin/templates/mediagoblin/utils/tags.html:20 -msgid "View more media tagged with" -msgstr "ZobraziÅ¥ viac výtvorov oznaÄených ako" - -#: mediagoblin/templates/mediagoblin/utils/tags.html:25 -msgid "or" -msgstr "alebo" +msgid "Tagged with" +msgstr "" #: mediagoblin/tools/exif.py:68 msgid "Could not read the image file." -msgstr "" +msgstr "Nebolo možné preÄÃtaÅ¥ obrazový súbor." #: mediagoblin/user_pages/forms.py:30 msgid "I am sure I want to delete this" msgstr "JednoznaÄne to chcem odstrániÅ¥" -#: mediagoblin/user_pages/views.py:155 +#: mediagoblin/user_pages/views.py:153 msgid "Oops, your comment was empty." msgstr "Ajaj, tvoj komentár bol prázdny." -#: mediagoblin/user_pages/views.py:161 +#: mediagoblin/user_pages/views.py:159 msgid "Your comment has been posted!" msgstr "Tvoj komentár bol zaslaný!" -#: mediagoblin/user_pages/views.py:183 +#: mediagoblin/user_pages/views.py:185 msgid "You deleted the media." msgstr "Výtvor bol tebou odstránený." -#: mediagoblin/user_pages/views.py:190 +#: mediagoblin/user_pages/views.py:192 msgid "The media was not deleted because you didn't check that you were sure." msgstr "Výtvor nebol odstránený, nakoľko chýbalo tvoje potvrdenie." -#: mediagoblin/user_pages/views.py:198 +#: mediagoblin/user_pages/views.py:200 msgid "You are about to delete another user's media. Proceed with caution." msgstr "Chystáš sa odstrániÅ¥ výtvory niekoho iného. Dbaj na to." - - diff --git a/mediagoblin/i18n/sl/LC_MESSAGES/mediagoblin.mo b/mediagoblin/i18n/sl/LC_MESSAGES/mediagoblin.mo Binary files differindex daa6b23b..b94affff 100644 --- a/mediagoblin/i18n/sl/LC_MESSAGES/mediagoblin.mo +++ b/mediagoblin/i18n/sl/LC_MESSAGES/mediagoblin.mo diff --git a/mediagoblin/i18n/sl/LC_MESSAGES/mediagoblin.po b/mediagoblin/i18n/sl/LC_MESSAGES/mediagoblin.po index 35fb4841..eacb4e67 100644 --- a/mediagoblin/i18n/sl/LC_MESSAGES/mediagoblin.po +++ b/mediagoblin/i18n/sl/LC_MESSAGES/mediagoblin.po @@ -7,9 +7,9 @@ msgid "" msgstr "" "Project-Id-Version: GNU MediaGoblin\n" -"Report-Msgid-Bugs-To: http://bugs.foocorp.net/projects/mediagoblin/issues\n" -"POT-Creation-Date: 2012-01-29 13:31-0600\n" -"PO-Revision-Date: 2012-01-29 19:29+0000\n" +"Report-Msgid-Bugs-To: http://issues.mediagoblin.org/\n" +"POT-Creation-Date: 2012-03-18 15:16-0500\n" +"PO-Revision-Date: 2012-03-18 20:14+0000\n" "Last-Translator: cwebber <cwebber@dustycloud.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n" "MIME-Version: 1.0\n" @@ -19,7 +19,7 @@ msgstr "" "Language: sl\n" "Plural-Forms: nplurals=4; plural=(n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n%100==4 ? 2 : 3)\n" -#: mediagoblin/processing.py:143 +#: mediagoblin/processing.py:154 msgid "Invalid file given for media type." msgstr "Za vrsto vsebine je bila podana napaÄna datoteka." @@ -39,54 +39,52 @@ msgstr "E-poÅ¡tni naslov" msgid "Sorry, registration is disabled on this instance." msgstr "Oprostite, prijava za ta izvod ni omogoÄena." -#: mediagoblin/auth/views.py:73 +#: mediagoblin/auth/views.py:75 msgid "Sorry, a user with that name already exists." msgstr "Oprostite, uporabnik s tem imenom že obstaja." -#: mediagoblin/auth/views.py:77 +#: mediagoblin/auth/views.py:79 msgid "Sorry, a user with that email address already exists." msgstr "" -#: mediagoblin/auth/views.py:180 +#: mediagoblin/auth/views.py:182 msgid "" "Your email address has been verified. You may now login, edit your profile, " "and submit images!" -msgstr "" -"VaÅ¡ e-poÅ¡tni naslov je bil potrjen. Sedaj se lahko prijavite, uredite svoj " -"profil in poÅ¡ljete slike." +msgstr "VaÅ¡ e-poÅ¡tni naslov je bil potrjen. Sedaj se lahko prijavite, uredite svoj profil in poÅ¡ljete slike." -#: mediagoblin/auth/views.py:186 +#: mediagoblin/auth/views.py:188 msgid "The verification key or user id is incorrect" msgstr "Potrditveni kljuÄ ali uporabniÅ¡ka identifikacija je napaÄna" -#: mediagoblin/auth/views.py:204 +#: mediagoblin/auth/views.py:206 msgid "You must be logged in so we know who to send the email to!" msgstr "" -#: mediagoblin/auth/views.py:212 +#: mediagoblin/auth/views.py:214 msgid "You've already verified your email address!" msgstr "" -#: mediagoblin/auth/views.py:225 +#: mediagoblin/auth/views.py:227 msgid "Resent your verification email." msgstr "Ponovno poÅ¡iljanje potrditvene e-poÅ¡te." -#: mediagoblin/auth/views.py:260 +#: mediagoblin/auth/views.py:262 msgid "" "An email has been sent with instructions on how to change your password." msgstr "" -#: mediagoblin/auth/views.py:270 +#: mediagoblin/auth/views.py:272 msgid "" "Could not send password recovery email as your username is inactive or your " "account's email address has not been verified." msgstr "" -#: mediagoblin/auth/views.py:282 +#: mediagoblin/auth/views.py:284 msgid "Couldn't find someone with that username or email." msgstr "" -#: mediagoblin/auth/views.py:330 +#: mediagoblin/auth/views.py:332 msgid "You can now log in using your new password." msgstr "" @@ -129,6 +127,7 @@ msgid "" msgstr "" #: mediagoblin/edit/forms.py:44 mediagoblin/submit/forms.py:41 +#: mediagoblin/templates/mediagoblin/utils/license.html:20 msgid "License" msgstr "" @@ -152,27 +151,27 @@ msgstr "" msgid "New password" msgstr "" -#: mediagoblin/edit/views.py:68 +#: mediagoblin/edit/views.py:67 msgid "An entry with that slug already exists for this user." msgstr "Vnos s to oznako za tega uporabnika že obstaja." -#: mediagoblin/edit/views.py:92 +#: mediagoblin/edit/views.py:88 msgid "You are editing another user's media. Proceed with caution." msgstr "Urejate vsebino drugega uporabnika. Nadaljujte pazljivo." -#: mediagoblin/edit/views.py:162 +#: mediagoblin/edit/views.py:158 msgid "You are editing a user's profile. Proceed with caution." msgstr "Urejate uporabniÅ¡ki profil. Nadaljujte pazljivo." -#: mediagoblin/edit/views.py:180 +#: mediagoblin/edit/views.py:174 msgid "Profile changes saved" msgstr "" -#: mediagoblin/edit/views.py:206 +#: mediagoblin/edit/views.py:200 msgid "Wrong password" msgstr "" -#: mediagoblin/edit/views.py:222 +#: mediagoblin/edit/views.py:216 msgid "Account settings saved" msgstr "" @@ -192,7 +191,7 @@ msgstr "Datoteka" msgid "You must provide a file." msgstr "Podati morate datoteko." -#: mediagoblin/submit/views.py:158 +#: mediagoblin/submit/views.py:156 msgid "Woohoo! Submitted!" msgstr "Juhej! Poslano." @@ -212,9 +211,7 @@ msgstr "Oprostite. Videti je, da na tem naslovu ni nobene strani." msgid "" "If you're sure the address is correct, maybe the page you're looking for has" " been moved or deleted." -msgstr "" -"ÄŒe ste v toÄnost naslova prepriÄani, je bila iskana stran morda premaknjena " -"ali pa izbrisana." +msgstr "ÄŒe ste v toÄnost naslova prepriÄani, je bila iskana stran morda premaknjena ali pa izbrisana." #: mediagoblin/templates/mediagoblin/base.html:46 msgid "MediaGoblin logo" @@ -242,7 +239,15 @@ msgstr "Prijava" #: mediagoblin/templates/mediagoblin/base.html:84 msgid "" "Powered by <a href=\"http://mediagoblin.org\">MediaGoblin</a>, a <a " -"href=\"http://gnu.org/\">GNU</a> project" +"href=\"http://gnu.org/\">GNU</a> project." +msgstr "" + +#: mediagoblin/templates/mediagoblin/base.html:87 +#, python-format +msgid "" +"Released under the <a " +"href=\"http://www.fsf.org/licensing/licenses/agpl-3.0.html\">AGPL</a>. <a " +"href=\"%(source_link)s\">Source code</a> available." msgstr "" #: mediagoblin/templates/mediagoblin/root.html:24 @@ -344,13 +349,7 @@ msgid "" "your web browser:\n" "\n" "%(verification_url)s" -msgstr "" -"Pozdravljeni, %(username)s\n" -"\n" -"Za aktivacijo svojega raÄuna GNU MediaGoblin odprite\n" -"naslednji URL v svojem spletnem brskalniku:\n" -"\n" -"%(verification_url)s" +msgstr "Pozdravljeni, %(username)s\n\nZa aktivacijo svojega raÄuna GNU MediaGoblin odprite\nnaslednji URL v svojem spletnem brskalniku:\n\n%(verification_url)s" #: mediagoblin/templates/mediagoblin/edit/edit.html:29 #, python-format @@ -385,18 +384,18 @@ msgid "Media tagged with: %(tag_name)s" msgstr "" #: mediagoblin/templates/mediagoblin/media_displays/ascii.html:34 -#: mediagoblin/templates/mediagoblin/media_displays/video.html:46 +#: mediagoblin/templates/mediagoblin/media_displays/video.html:53 msgid "Original" msgstr "" -#: mediagoblin/templates/mediagoblin/media_displays/video.html:33 +#: mediagoblin/templates/mediagoblin/media_displays/video.html:40 msgid "" "Sorry, this video will not work because \n" "\t your web browser does not support HTML5 \n" "\t video." msgstr "" -#: mediagoblin/templates/mediagoblin/media_displays/video.html:36 +#: mediagoblin/templates/mediagoblin/media_displays/video.html:43 msgid "" "You can get a modern web browser that \n" "\t can play this video at <a href=\"http://getfirefox.com\">\n" @@ -421,55 +420,43 @@ msgstr "" msgid "<a href=\"%(user_url)s\">%(username)s</a>'s media" msgstr "Vsebina uporabnika <a href=\"%(user_url)s\">%(username)s</a>" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:72 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:46 #, python-format -msgid "Added on %(date)s." +msgid "â– Browsing media by <a href=\"%(user_url)s\">%(username)s</a>" msgstr "" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:81 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:85 msgid "Edit" msgstr "" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:85 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:89 msgid "Delete" msgstr "" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:91 -#, python-format -msgid "%(comment_count)s comment" -msgstr "" - -#: mediagoblin/templates/mediagoblin/user_pages/media.html:93 -#, python-format -msgid "%(comment_count)s comments" -msgstr "" - -#: mediagoblin/templates/mediagoblin/user_pages/media.html:95 -msgid "No comments yet." -msgstr "" - -#: mediagoblin/templates/mediagoblin/user_pages/media.html:103 -msgid "Add one" +#: mediagoblin/templates/mediagoblin/user_pages/media.html:124 +msgid "Add a comment" msgstr "" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:112 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:133 msgid "" "You can use <a " "href=\"http://daringfireball.net/projects/markdown/basics\">Markdown</a> for" " formatting." msgstr "" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:116 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:137 msgid "Add this comment" msgstr "" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:138 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:159 msgid "at" msgstr "" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:153 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:174 #, python-format -msgid "<p>â– Browsing media by <a href=\"%(user_url)s\">%(username)s</a></p>" +msgid "" +"<h3>Added on</h3>\n" +" <p>%(date)s</p>" msgstr "" #: mediagoblin/templates/mediagoblin/user_pages/media_confirm_delete.html:30 @@ -478,7 +465,7 @@ msgid "Really delete %(title)s?" msgstr "" #: mediagoblin/templates/mediagoblin/user_pages/media_confirm_delete.html:50 -msgid "Delete Permanently" +msgid "Delete permanently" msgstr "" #: mediagoblin/templates/mediagoblin/user_pages/processing_panel.html:22 @@ -538,18 +525,14 @@ msgstr "Ponovno poÅ¡lji potrditveno e-poÅ¡to" msgid "" "Someone has registered an account with this username, but it still has to be" " activated." -msgstr "" -"Nekdo je s tem uporabniÅ¡kim imenom že registriral raÄun, vendar mora biti Å¡e" -" aktiviran." +msgstr "Nekdo je s tem uporabniÅ¡kim imenom že registriral raÄun, vendar mora biti Å¡e aktiviran." #: mediagoblin/templates/mediagoblin/user_pages/user.html:79 #, python-format msgid "" "If you are that person but you've lost your verification email, you can <a " "href=\"%(login_url)s\">log in</a> and resend it." -msgstr "" -"ÄŒe ste ta oseba vi, a ste izgubili potrditveno e-poÅ¡to, se lahko <a " -"href=\"%(login_url)s\">prijavite</a> in jo ponovno poÅ¡ljete." +msgstr "ÄŒe ste ta oseba vi, a ste izgubili potrditveno e-poÅ¡to, se lahko <a href=\"%(login_url)s\">prijavite</a> in jo ponovno poÅ¡ljete." #: mediagoblin/templates/mediagoblin/user_pages/user.html:96 msgid "Here's a spot to tell others about yourself." @@ -592,8 +575,13 @@ msgstr "Ikona vira" msgid "Atom feed" msgstr "Ikona Atom" -#: mediagoblin/templates/mediagoblin/utils/license.html:21 -msgid "License:" +#: mediagoblin/templates/mediagoblin/utils/geolocation_map.html:25 +msgid "Location" +msgstr "" + +#: mediagoblin/templates/mediagoblin/utils/geolocation_map.html:38 +#, python-format +msgid "View on <a href=\"%(osm_url)s\">OpenStreetMap</a>" msgstr "" #: mediagoblin/templates/mediagoblin/utils/license.html:25 @@ -612,22 +600,18 @@ msgstr "" msgid "Go to page:" msgstr "" -#: mediagoblin/templates/mediagoblin/utils/prev_next.html:27 -#: mediagoblin/templates/mediagoblin/utils/prev_next.html:32 +#: mediagoblin/templates/mediagoblin/utils/prev_next.html:28 +#: mediagoblin/templates/mediagoblin/utils/prev_next.html:33 msgid "newer" msgstr "" -#: mediagoblin/templates/mediagoblin/utils/prev_next.html:38 -#: mediagoblin/templates/mediagoblin/utils/prev_next.html:43 +#: mediagoblin/templates/mediagoblin/utils/prev_next.html:39 +#: mediagoblin/templates/mediagoblin/utils/prev_next.html:44 msgid "older" msgstr "" #: mediagoblin/templates/mediagoblin/utils/tags.html:20 -msgid "View more media tagged with" -msgstr "" - -#: mediagoblin/templates/mediagoblin/utils/tags.html:25 -msgid "or" +msgid "Tagged with" msgstr "" #: mediagoblin/tools/exif.py:68 @@ -638,24 +622,22 @@ msgstr "" msgid "I am sure I want to delete this" msgstr "" -#: mediagoblin/user_pages/views.py:155 +#: mediagoblin/user_pages/views.py:153 msgid "Oops, your comment was empty." msgstr "" -#: mediagoblin/user_pages/views.py:161 +#: mediagoblin/user_pages/views.py:159 msgid "Your comment has been posted!" msgstr "" -#: mediagoblin/user_pages/views.py:183 +#: mediagoblin/user_pages/views.py:185 msgid "You deleted the media." msgstr "" -#: mediagoblin/user_pages/views.py:190 +#: mediagoblin/user_pages/views.py:192 msgid "The media was not deleted because you didn't check that you were sure." msgstr "" -#: mediagoblin/user_pages/views.py:198 +#: mediagoblin/user_pages/views.py:200 msgid "You are about to delete another user's media. Proceed with caution." msgstr "" - - diff --git a/mediagoblin/i18n/sr/LC_MESSAGES/mediagoblin.mo b/mediagoblin/i18n/sr/LC_MESSAGES/mediagoblin.mo Binary files differindex 8191444c..a94e2833 100644 --- a/mediagoblin/i18n/sr/LC_MESSAGES/mediagoblin.mo +++ b/mediagoblin/i18n/sr/LC_MESSAGES/mediagoblin.mo diff --git a/mediagoblin/i18n/sr/LC_MESSAGES/mediagoblin.po b/mediagoblin/i18n/sr/LC_MESSAGES/mediagoblin.po index e0f59766..7a53adfd 100644 --- a/mediagoblin/i18n/sr/LC_MESSAGES/mediagoblin.po +++ b/mediagoblin/i18n/sr/LC_MESSAGES/mediagoblin.po @@ -6,11 +6,11 @@ msgid "" msgstr "" "Project-Id-Version: GNU MediaGoblin\n" -"Report-Msgid-Bugs-To: http://bugs.foocorp.net/projects/mediagoblin/issues\n" -"POT-Creation-Date: 2012-01-29 13:31-0600\n" -"PO-Revision-Date: 2012-01-29 19:29+0000\n" +"Report-Msgid-Bugs-To: http://issues.mediagoblin.org/\n" +"POT-Creation-Date: 2012-03-18 15:16-0500\n" +"PO-Revision-Date: 2012-03-18 20:14+0000\n" "Last-Translator: cwebber <cwebber@dustycloud.org>\n" -"Language-Team: Serbian (http://www.transifex.net/projects/p/mediagoblin/team/sr/)\n" +"Language-Team: Serbian (http://www.transifex.net/projects/p/mediagoblin/language/sr/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" @@ -18,7 +18,7 @@ msgstr "" "Language: sr\n" "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)\n" -#: mediagoblin/processing.py:143 +#: mediagoblin/processing.py:154 msgid "Invalid file given for media type." msgstr "" @@ -38,52 +38,52 @@ msgstr "" msgid "Sorry, registration is disabled on this instance." msgstr "" -#: mediagoblin/auth/views.py:73 +#: mediagoblin/auth/views.py:75 msgid "Sorry, a user with that name already exists." msgstr "" -#: mediagoblin/auth/views.py:77 +#: mediagoblin/auth/views.py:79 msgid "Sorry, a user with that email address already exists." msgstr "" -#: mediagoblin/auth/views.py:180 +#: mediagoblin/auth/views.py:182 msgid "" "Your email address has been verified. You may now login, edit your profile, " "and submit images!" msgstr "" -#: mediagoblin/auth/views.py:186 +#: mediagoblin/auth/views.py:188 msgid "The verification key or user id is incorrect" msgstr "" -#: mediagoblin/auth/views.py:204 +#: mediagoblin/auth/views.py:206 msgid "You must be logged in so we know who to send the email to!" msgstr "" -#: mediagoblin/auth/views.py:212 +#: mediagoblin/auth/views.py:214 msgid "You've already verified your email address!" msgstr "" -#: mediagoblin/auth/views.py:225 +#: mediagoblin/auth/views.py:227 msgid "Resent your verification email." msgstr "" -#: mediagoblin/auth/views.py:260 +#: mediagoblin/auth/views.py:262 msgid "" "An email has been sent with instructions on how to change your password." msgstr "" -#: mediagoblin/auth/views.py:270 +#: mediagoblin/auth/views.py:272 msgid "" "Could not send password recovery email as your username is inactive or your " "account's email address has not been verified." msgstr "" -#: mediagoblin/auth/views.py:282 +#: mediagoblin/auth/views.py:284 msgid "Couldn't find someone with that username or email." msgstr "" -#: mediagoblin/auth/views.py:330 +#: mediagoblin/auth/views.py:332 msgid "You can now log in using your new password." msgstr "" @@ -126,6 +126,7 @@ msgid "" msgstr "" #: mediagoblin/edit/forms.py:44 mediagoblin/submit/forms.py:41 +#: mediagoblin/templates/mediagoblin/utils/license.html:20 msgid "License" msgstr "" @@ -149,27 +150,27 @@ msgstr "" msgid "New password" msgstr "" -#: mediagoblin/edit/views.py:68 +#: mediagoblin/edit/views.py:67 msgid "An entry with that slug already exists for this user." msgstr "" -#: mediagoblin/edit/views.py:92 +#: mediagoblin/edit/views.py:88 msgid "You are editing another user's media. Proceed with caution." msgstr "" -#: mediagoblin/edit/views.py:162 +#: mediagoblin/edit/views.py:158 msgid "You are editing a user's profile. Proceed with caution." msgstr "" -#: mediagoblin/edit/views.py:180 +#: mediagoblin/edit/views.py:174 msgid "Profile changes saved" msgstr "" -#: mediagoblin/edit/views.py:206 +#: mediagoblin/edit/views.py:200 msgid "Wrong password" msgstr "" -#: mediagoblin/edit/views.py:222 +#: mediagoblin/edit/views.py:216 msgid "Account settings saved" msgstr "" @@ -189,7 +190,7 @@ msgstr "" msgid "You must provide a file." msgstr "" -#: mediagoblin/submit/views.py:158 +#: mediagoblin/submit/views.py:156 msgid "Woohoo! Submitted!" msgstr "" @@ -237,7 +238,15 @@ msgstr "" #: mediagoblin/templates/mediagoblin/base.html:84 msgid "" "Powered by <a href=\"http://mediagoblin.org\">MediaGoblin</a>, a <a " -"href=\"http://gnu.org/\">GNU</a> project" +"href=\"http://gnu.org/\">GNU</a> project." +msgstr "" + +#: mediagoblin/templates/mediagoblin/base.html:87 +#, python-format +msgid "" +"Released under the <a " +"href=\"http://www.fsf.org/licensing/licenses/agpl-3.0.html\">AGPL</a>. <a " +"href=\"%(source_link)s\">Source code</a> available." msgstr "" #: mediagoblin/templates/mediagoblin/root.html:24 @@ -374,18 +383,18 @@ msgid "Media tagged with: %(tag_name)s" msgstr "" #: mediagoblin/templates/mediagoblin/media_displays/ascii.html:34 -#: mediagoblin/templates/mediagoblin/media_displays/video.html:46 +#: mediagoblin/templates/mediagoblin/media_displays/video.html:53 msgid "Original" msgstr "" -#: mediagoblin/templates/mediagoblin/media_displays/video.html:33 +#: mediagoblin/templates/mediagoblin/media_displays/video.html:40 msgid "" "Sorry, this video will not work because \n" "\t your web browser does not support HTML5 \n" "\t video." msgstr "" -#: mediagoblin/templates/mediagoblin/media_displays/video.html:36 +#: mediagoblin/templates/mediagoblin/media_displays/video.html:43 msgid "" "You can get a modern web browser that \n" "\t can play this video at <a href=\"http://getfirefox.com\">\n" @@ -410,55 +419,43 @@ msgstr "" msgid "<a href=\"%(user_url)s\">%(username)s</a>'s media" msgstr "" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:72 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:46 #, python-format -msgid "Added on %(date)s." +msgid "â– Browsing media by <a href=\"%(user_url)s\">%(username)s</a>" msgstr "" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:81 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:85 msgid "Edit" msgstr "" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:85 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:89 msgid "Delete" msgstr "" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:91 -#, python-format -msgid "%(comment_count)s comment" -msgstr "" - -#: mediagoblin/templates/mediagoblin/user_pages/media.html:93 -#, python-format -msgid "%(comment_count)s comments" -msgstr "" - -#: mediagoblin/templates/mediagoblin/user_pages/media.html:95 -msgid "No comments yet." -msgstr "" - -#: mediagoblin/templates/mediagoblin/user_pages/media.html:103 -msgid "Add one" +#: mediagoblin/templates/mediagoblin/user_pages/media.html:124 +msgid "Add a comment" msgstr "" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:112 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:133 msgid "" "You can use <a " "href=\"http://daringfireball.net/projects/markdown/basics\">Markdown</a> for" " formatting." msgstr "" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:116 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:137 msgid "Add this comment" msgstr "" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:138 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:159 msgid "at" msgstr "" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:153 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:174 #, python-format -msgid "<p>â– Browsing media by <a href=\"%(user_url)s\">%(username)s</a></p>" +msgid "" +"<h3>Added on</h3>\n" +" <p>%(date)s</p>" msgstr "" #: mediagoblin/templates/mediagoblin/user_pages/media_confirm_delete.html:30 @@ -467,7 +464,7 @@ msgid "Really delete %(title)s?" msgstr "" #: mediagoblin/templates/mediagoblin/user_pages/media_confirm_delete.html:50 -msgid "Delete Permanently" +msgid "Delete permanently" msgstr "" #: mediagoblin/templates/mediagoblin/user_pages/processing_panel.html:22 @@ -577,8 +574,13 @@ msgstr "" msgid "Atom feed" msgstr "" -#: mediagoblin/templates/mediagoblin/utils/license.html:21 -msgid "License:" +#: mediagoblin/templates/mediagoblin/utils/geolocation_map.html:25 +msgid "Location" +msgstr "" + +#: mediagoblin/templates/mediagoblin/utils/geolocation_map.html:38 +#, python-format +msgid "View on <a href=\"%(osm_url)s\">OpenStreetMap</a>" msgstr "" #: mediagoblin/templates/mediagoblin/utils/license.html:25 @@ -597,22 +599,18 @@ msgstr "" msgid "Go to page:" msgstr "" -#: mediagoblin/templates/mediagoblin/utils/prev_next.html:27 -#: mediagoblin/templates/mediagoblin/utils/prev_next.html:32 +#: mediagoblin/templates/mediagoblin/utils/prev_next.html:28 +#: mediagoblin/templates/mediagoblin/utils/prev_next.html:33 msgid "newer" msgstr "" -#: mediagoblin/templates/mediagoblin/utils/prev_next.html:38 -#: mediagoblin/templates/mediagoblin/utils/prev_next.html:43 +#: mediagoblin/templates/mediagoblin/utils/prev_next.html:39 +#: mediagoblin/templates/mediagoblin/utils/prev_next.html:44 msgid "older" msgstr "" #: mediagoblin/templates/mediagoblin/utils/tags.html:20 -msgid "View more media tagged with" -msgstr "" - -#: mediagoblin/templates/mediagoblin/utils/tags.html:25 -msgid "or" +msgid "Tagged with" msgstr "" #: mediagoblin/tools/exif.py:68 @@ -623,24 +621,22 @@ msgstr "" msgid "I am sure I want to delete this" msgstr "" -#: mediagoblin/user_pages/views.py:155 +#: mediagoblin/user_pages/views.py:153 msgid "Oops, your comment was empty." msgstr "" -#: mediagoblin/user_pages/views.py:161 +#: mediagoblin/user_pages/views.py:159 msgid "Your comment has been posted!" msgstr "" -#: mediagoblin/user_pages/views.py:183 +#: mediagoblin/user_pages/views.py:185 msgid "You deleted the media." msgstr "" -#: mediagoblin/user_pages/views.py:190 +#: mediagoblin/user_pages/views.py:192 msgid "The media was not deleted because you didn't check that you were sure." msgstr "" -#: mediagoblin/user_pages/views.py:198 +#: mediagoblin/user_pages/views.py:200 msgid "You are about to delete another user's media. Proceed with caution." msgstr "" - - diff --git a/mediagoblin/i18n/sv/LC_MESSAGES/mediagoblin.mo b/mediagoblin/i18n/sv/LC_MESSAGES/mediagoblin.mo Binary files differindex 095deaa2..acba5cdb 100644 --- a/mediagoblin/i18n/sv/LC_MESSAGES/mediagoblin.mo +++ b/mediagoblin/i18n/sv/LC_MESSAGES/mediagoblin.mo diff --git a/mediagoblin/i18n/sv/LC_MESSAGES/mediagoblin.po b/mediagoblin/i18n/sv/LC_MESSAGES/mediagoblin.po index 91791796..f820de8a 100644 --- a/mediagoblin/i18n/sv/LC_MESSAGES/mediagoblin.po +++ b/mediagoblin/i18n/sv/LC_MESSAGES/mediagoblin.po @@ -8,11 +8,11 @@ msgid "" msgstr "" "Project-Id-Version: GNU MediaGoblin\n" -"Report-Msgid-Bugs-To: http://bugs.foocorp.net/projects/mediagoblin/issues\n" -"POT-Creation-Date: 2012-01-29 13:31-0600\n" -"PO-Revision-Date: 2012-01-29 19:29+0000\n" +"Report-Msgid-Bugs-To: http://issues.mediagoblin.org/\n" +"POT-Creation-Date: 2012-03-18 15:16-0500\n" +"PO-Revision-Date: 2012-03-18 20:14+0000\n" "Last-Translator: cwebber <cwebber@dustycloud.org>\n" -"Language-Team: Swedish (http://www.transifex.net/projects/p/mediagoblin/team/sv/)\n" +"Language-Team: Swedish (http://www.transifex.net/projects/p/mediagoblin/language/sv/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" @@ -20,7 +20,7 @@ msgstr "" "Language: sv\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" -#: mediagoblin/processing.py:143 +#: mediagoblin/processing.py:154 msgid "Invalid file given for media type." msgstr "Ogiltig fil för mediatypen." @@ -40,57 +40,52 @@ msgstr "E-postadress" msgid "Sorry, registration is disabled on this instance." msgstr "Vi beklagar, registreringen är avtängd pÃ¥ den här instansen." -#: mediagoblin/auth/views.py:73 +#: mediagoblin/auth/views.py:75 msgid "Sorry, a user with that name already exists." msgstr "En användare med det användarnamnet finns redan." -#: mediagoblin/auth/views.py:77 +#: mediagoblin/auth/views.py:79 msgid "Sorry, a user with that email address already exists." msgstr "Det finns redan en användare med den e-postadressen." -#: mediagoblin/auth/views.py:180 +#: mediagoblin/auth/views.py:182 msgid "" "Your email address has been verified. You may now login, edit your profile, " "and submit images!" -msgstr "" -"Din e-postadress är verifierad. Du kan nu logga in, redigera din profil och " -"ladda upp filer!" +msgstr "Din e-postadress är verifierad. Du kan nu logga in, redigera din profil och ladda upp filer!" -#: mediagoblin/auth/views.py:186 +#: mediagoblin/auth/views.py:188 msgid "The verification key or user id is incorrect" msgstr "Verifieringsnyckeln eller användar-IDt är fel." -#: mediagoblin/auth/views.py:204 +#: mediagoblin/auth/views.py:206 msgid "You must be logged in so we know who to send the email to!" -msgstr "" -"Du mÃ¥ste vara inloggad för att vi ska kunna skicka meddelandet till dig." +msgstr "Du mÃ¥ste vara inloggad för att vi ska kunna skicka meddelandet till dig." -#: mediagoblin/auth/views.py:212 +#: mediagoblin/auth/views.py:214 msgid "You've already verified your email address!" msgstr "Du har redan verifierat din e-postadress!" -#: mediagoblin/auth/views.py:225 +#: mediagoblin/auth/views.py:227 msgid "Resent your verification email." msgstr "Skickade ett nytt verifierings-email." -#: mediagoblin/auth/views.py:260 +#: mediagoblin/auth/views.py:262 msgid "" "An email has been sent with instructions on how to change your password." msgstr "" -#: mediagoblin/auth/views.py:270 +#: mediagoblin/auth/views.py:272 msgid "" "Could not send password recovery email as your username is inactive or your " "account's email address has not been verified." -msgstr "" -"Kunde inte skicka e-postÃ¥terställning av lösenord eftersom ditt användarnamn" -" är inaktivt eller kontots e-postadress har inte verifierats." +msgstr "Kunde inte skicka e-postÃ¥terställning av lösenord eftersom ditt användarnamn är inaktivt eller kontots e-postadress har inte verifierats." -#: mediagoblin/auth/views.py:282 +#: mediagoblin/auth/views.py:284 msgid "Couldn't find someone with that username or email." msgstr "" -#: mediagoblin/auth/views.py:330 +#: mediagoblin/auth/views.py:332 msgid "You can now log in using your new password." msgstr "" @@ -133,6 +128,7 @@ msgid "" msgstr "" #: mediagoblin/edit/forms.py:44 mediagoblin/submit/forms.py:41 +#: mediagoblin/templates/mediagoblin/utils/license.html:20 msgid "License" msgstr "" @@ -156,27 +152,27 @@ msgstr "" msgid "New password" msgstr "" -#: mediagoblin/edit/views.py:68 +#: mediagoblin/edit/views.py:67 msgid "An entry with that slug already exists for this user." msgstr "Ett inlägg med det sökvägsnamnet existerar redan." -#: mediagoblin/edit/views.py:92 +#: mediagoblin/edit/views.py:88 msgid "You are editing another user's media. Proceed with caution." msgstr "Var försiktig, du redigerar nÃ¥gon annans inlägg." -#: mediagoblin/edit/views.py:162 +#: mediagoblin/edit/views.py:158 msgid "You are editing a user's profile. Proceed with caution." msgstr "Var försiktig, du redigerar en annan användares profil." -#: mediagoblin/edit/views.py:180 +#: mediagoblin/edit/views.py:174 msgid "Profile changes saved" msgstr "" -#: mediagoblin/edit/views.py:206 +#: mediagoblin/edit/views.py:200 msgid "Wrong password" msgstr "Fel lösenord" -#: mediagoblin/edit/views.py:222 +#: mediagoblin/edit/views.py:216 msgid "Account settings saved" msgstr "" @@ -196,7 +192,7 @@ msgstr "Fil" msgid "You must provide a file." msgstr "Du mÃ¥ste ange en fil" -#: mediagoblin/submit/views.py:158 +#: mediagoblin/submit/views.py:156 msgid "Woohoo! Submitted!" msgstr "Tjohoo! Upladdat!" @@ -216,9 +212,7 @@ msgstr "Ledsen, det verkar inte vara nÃ¥gonting här." msgid "" "If you're sure the address is correct, maybe the page you're looking for has" " been moved or deleted." -msgstr "" -"Om du är säker pÃ¥ att adressen stämmer sÃ¥ kanske sidan du letar efter har " -"flyttats eller tagits bort." +msgstr "Om du är säker pÃ¥ att adressen stämmer sÃ¥ kanske sidan du letar efter har flyttats eller tagits bort." #: mediagoblin/templates/mediagoblin/base.html:46 msgid "MediaGoblin logo" @@ -246,10 +240,16 @@ msgstr "Logga in" #: mediagoblin/templates/mediagoblin/base.html:84 msgid "" "Powered by <a href=\"http://mediagoblin.org\">MediaGoblin</a>, a <a " -"href=\"http://gnu.org/\">GNU</a> project" +"href=\"http://gnu.org/\">GNU</a> project." +msgstr "" + +#: mediagoblin/templates/mediagoblin/base.html:87 +#, python-format +msgid "" +"Released under the <a " +"href=\"http://www.fsf.org/licensing/licenses/agpl-3.0.html\">AGPL</a>. <a " +"href=\"%(source_link)s\">Source code</a> available." msgstr "" -"Drivs av <a href=\"http://mediagoblin.org\">MediaGoblin</a>, ett <a " -"href=\"http://gnu.org/\">GNU</a>-projekt" #: mediagoblin/templates/mediagoblin/root.html:24 msgid "Explore" @@ -315,15 +315,7 @@ msgid "" "\n" "If you think this is an error, just ignore this email and continue being\n" "a happy goblin!" -msgstr "" -"Hej %(username)s,\n" -"\n" -"för att ändra ditt GNU MediaGoblin-lösenord, öppna följande länk i\n" -"din webbläsare:\n" -"\n" -"%(verification_url)s\n" -"\n" -"Om du misstänker att du fÃ¥tt detta epostmeddelanade av misstag, ignorera det och fortsätt vara ett glatt troll!" +msgstr "Hej %(username)s,\n\nför att ändra ditt GNU MediaGoblin-lösenord, öppna följande länk i\ndin webbläsare:\n\n%(verification_url)s\n\nOm du misstänker att du fÃ¥tt detta epostmeddelanade av misstag, ignorera det och fortsätt vara ett glatt troll!" #: mediagoblin/templates/mediagoblin/auth/login.html:30 msgid "Logging in failed!" @@ -358,12 +350,7 @@ msgid "" "your web browser:\n" "\n" "%(verification_url)s" -msgstr "" -"Hej %(username)s,\n" -"\n" -"öppna den följande webbadressen i din webbläsare för att aktivera ditt konto pÃ¥ GNU MediaGoblin:\n" -"\n" -"%(verification_url)s" +msgstr "Hej %(username)s,\n\nöppna den följande webbadressen i din webbläsare för att aktivera ditt konto pÃ¥ GNU MediaGoblin:\n\n%(verification_url)s" #: mediagoblin/templates/mediagoblin/edit/edit.html:29 #, python-format @@ -398,18 +385,18 @@ msgid "Media tagged with: %(tag_name)s" msgstr "Media taggat med: %(tag_name)s" #: mediagoblin/templates/mediagoblin/media_displays/ascii.html:34 -#: mediagoblin/templates/mediagoblin/media_displays/video.html:46 +#: mediagoblin/templates/mediagoblin/media_displays/video.html:53 msgid "Original" msgstr "Original" -#: mediagoblin/templates/mediagoblin/media_displays/video.html:33 +#: mediagoblin/templates/mediagoblin/media_displays/video.html:40 msgid "" "Sorry, this video will not work because \n" "\t your web browser does not support HTML5 \n" "\t video." msgstr "" -#: mediagoblin/templates/mediagoblin/media_displays/video.html:36 +#: mediagoblin/templates/mediagoblin/media_displays/video.html:43 msgid "" "You can get a modern web browser that \n" "\t can play this video at <a href=\"http://getfirefox.com\">\n" @@ -434,55 +421,43 @@ msgstr "%(username)ss media" msgid "<a href=\"%(user_url)s\">%(username)s</a>'s media" msgstr "<a href=\"%(user_url)s\">%(username)s</a>s media" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:72 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:46 #, python-format -msgid "Added on %(date)s." +msgid "â– Browsing media by <a href=\"%(user_url)s\">%(username)s</a>" msgstr "" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:81 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:85 msgid "Edit" msgstr "" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:85 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:89 msgid "Delete" msgstr "" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:91 -#, python-format -msgid "%(comment_count)s comment" -msgstr "" - -#: mediagoblin/templates/mediagoblin/user_pages/media.html:93 -#, python-format -msgid "%(comment_count)s comments" -msgstr "" - -#: mediagoblin/templates/mediagoblin/user_pages/media.html:95 -msgid "No comments yet." +#: mediagoblin/templates/mediagoblin/user_pages/media.html:124 +msgid "Add a comment" msgstr "" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:103 -msgid "Add one" -msgstr "" - -#: mediagoblin/templates/mediagoblin/user_pages/media.html:112 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:133 msgid "" "You can use <a " "href=\"http://daringfireball.net/projects/markdown/basics\">Markdown</a> for" " formatting." msgstr "" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:116 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:137 msgid "Add this comment" msgstr "" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:138 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:159 msgid "at" msgstr "" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:153 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:174 #, python-format -msgid "<p>â– Browsing media by <a href=\"%(user_url)s\">%(username)s</a></p>" +msgid "" +"<h3>Added on</h3>\n" +" <p>%(date)s</p>" msgstr "" #: mediagoblin/templates/mediagoblin/user_pages/media_confirm_delete.html:30 @@ -491,8 +466,8 @@ msgid "Really delete %(title)s?" msgstr "Vill du verkligen radera %(title)s?" #: mediagoblin/templates/mediagoblin/user_pages/media_confirm_delete.html:50 -msgid "Delete Permanently" -msgstr "Radera permanent" +msgid "Delete permanently" +msgstr "" #: mediagoblin/templates/mediagoblin/user_pages/processing_panel.html:22 msgid "Media processing panel" @@ -537,8 +512,7 @@ msgstr "Nästan klar! Ditt konto behöver bara aktiveras." #: mediagoblin/templates/mediagoblin/user_pages/user.html:58 msgid "" "An email should arrive in a few moments with instructions on how to do so." -msgstr "" -"Ett e-postmeddelande med instruktioner kommer att hamna hos dig inom kort." +msgstr "Ett e-postmeddelande med instruktioner kommer att hamna hos dig inom kort." #: mediagoblin/templates/mediagoblin/user_pages/user.html:62 msgid "In case it doesn't:" @@ -552,19 +526,14 @@ msgstr "Skicka ett nytt e-postmeddelande" msgid "" "Someone has registered an account with this username, but it still has to be" " activated." -msgstr "" -"NÃ¥gon har redan registrerat ett konto med det här användarnamnet men det har" -" inte aktiverats." +msgstr "NÃ¥gon har redan registrerat ett konto med det här användarnamnet men det har inte aktiverats." #: mediagoblin/templates/mediagoblin/user_pages/user.html:79 #, python-format msgid "" "If you are that person but you've lost your verification email, you can <a " "href=\"%(login_url)s\">log in</a> and resend it." -msgstr "" -"Om det är du som är den personen och har förlorat ditt e-postmeddelande med " -"detaljer om hur du verifierar ditt konto sÃ¥ kan du <a " -"href=\"%(login_url)s\">logga in</a> och begära ett nytt." +msgstr "Om det är du som är den personen och har förlorat ditt e-postmeddelande med detaljer om hur du verifierar ditt konto sÃ¥ kan du <a href=\"%(login_url)s\">logga in</a> och begära ett nytt." #: mediagoblin/templates/mediagoblin/user_pages/user.html:96 msgid "Here's a spot to tell others about yourself." @@ -592,9 +561,7 @@ msgstr "Se all media frÃ¥n %(username)s" msgid "" "This is where your media will appear, but you don't seem to have added " "anything yet." -msgstr "" -"Här kommer din media att dyka upp, du verkar inte ha lagt till nÃ¥gonting " -"ännu." +msgstr "Här kommer din media att dyka upp, du verkar inte ha lagt till nÃ¥gonting ännu." #: mediagoblin/templates/mediagoblin/user_pages/user.html:163 #: mediagoblin/templates/mediagoblin/utils/object_gallery.html:72 @@ -609,8 +576,13 @@ msgstr "feed-ikon" msgid "Atom feed" msgstr "Atom-feed" -#: mediagoblin/templates/mediagoblin/utils/license.html:21 -msgid "License:" +#: mediagoblin/templates/mediagoblin/utils/geolocation_map.html:25 +msgid "Location" +msgstr "" + +#: mediagoblin/templates/mediagoblin/utils/geolocation_map.html:38 +#, python-format +msgid "View on <a href=\"%(osm_url)s\">OpenStreetMap</a>" msgstr "" #: mediagoblin/templates/mediagoblin/utils/license.html:25 @@ -629,22 +601,18 @@ msgstr "" msgid "Go to page:" msgstr "" -#: mediagoblin/templates/mediagoblin/utils/prev_next.html:27 -#: mediagoblin/templates/mediagoblin/utils/prev_next.html:32 +#: mediagoblin/templates/mediagoblin/utils/prev_next.html:28 +#: mediagoblin/templates/mediagoblin/utils/prev_next.html:33 msgid "newer" msgstr "" -#: mediagoblin/templates/mediagoblin/utils/prev_next.html:38 -#: mediagoblin/templates/mediagoblin/utils/prev_next.html:43 +#: mediagoblin/templates/mediagoblin/utils/prev_next.html:39 +#: mediagoblin/templates/mediagoblin/utils/prev_next.html:44 msgid "older" msgstr "" #: mediagoblin/templates/mediagoblin/utils/tags.html:20 -msgid "View more media tagged with" -msgstr "" - -#: mediagoblin/templates/mediagoblin/utils/tags.html:25 -msgid "or" +msgid "Tagged with" msgstr "" #: mediagoblin/tools/exif.py:68 @@ -655,24 +623,22 @@ msgstr "" msgid "I am sure I want to delete this" msgstr "Jag är säker pÃ¥ att jag vill radera detta" -#: mediagoblin/user_pages/views.py:155 +#: mediagoblin/user_pages/views.py:153 msgid "Oops, your comment was empty." msgstr "" -#: mediagoblin/user_pages/views.py:161 +#: mediagoblin/user_pages/views.py:159 msgid "Your comment has been posted!" msgstr "" -#: mediagoblin/user_pages/views.py:183 +#: mediagoblin/user_pages/views.py:185 msgid "You deleted the media." msgstr "" -#: mediagoblin/user_pages/views.py:190 +#: mediagoblin/user_pages/views.py:192 msgid "The media was not deleted because you didn't check that you were sure." msgstr "" -#: mediagoblin/user_pages/views.py:198 +#: mediagoblin/user_pages/views.py:200 msgid "You are about to delete another user's media. Proceed with caution." msgstr "Du tänker radera en annan användares media. Var försiktig." - - diff --git a/mediagoblin/i18n/te/LC_MESSAGES/mediagoblin.mo b/mediagoblin/i18n/te/LC_MESSAGES/mediagoblin.mo Binary files differindex 9467a3f1..513efb7d 100644 --- a/mediagoblin/i18n/te/LC_MESSAGES/mediagoblin.mo +++ b/mediagoblin/i18n/te/LC_MESSAGES/mediagoblin.mo diff --git a/mediagoblin/i18n/te/LC_MESSAGES/mediagoblin.po b/mediagoblin/i18n/te/LC_MESSAGES/mediagoblin.po index 24155bff..57b5f82d 100644 --- a/mediagoblin/i18n/te/LC_MESSAGES/mediagoblin.po +++ b/mediagoblin/i18n/te/LC_MESSAGES/mediagoblin.po @@ -7,9 +7,9 @@ msgid "" msgstr "" "Project-Id-Version: GNU MediaGoblin\n" -"Report-Msgid-Bugs-To: http://bugs.foocorp.net/projects/mediagoblin/issues\n" -"POT-Creation-Date: 2012-01-29 13:31-0600\n" -"PO-Revision-Date: 2012-01-29 19:29+0000\n" +"Report-Msgid-Bugs-To: http://issues.mediagoblin.org/\n" +"POT-Creation-Date: 2012-03-18 15:16-0500\n" +"PO-Revision-Date: 2012-03-18 20:14+0000\n" "Last-Translator: cwebber <cwebber@dustycloud.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n" "MIME-Version: 1.0\n" @@ -19,7 +19,7 @@ msgstr "" "Language: te\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" -#: mediagoblin/processing.py:143 +#: mediagoblin/processing.py:154 msgid "Invalid file given for media type." msgstr "" @@ -39,52 +39,52 @@ msgstr "ఈమెయిలౠచిరà±à°¨à°¾à°®à°¾" msgid "Sorry, registration is disabled on this instance." msgstr "" -#: mediagoblin/auth/views.py:73 +#: mediagoblin/auth/views.py:75 msgid "Sorry, a user with that name already exists." msgstr "" -#: mediagoblin/auth/views.py:77 +#: mediagoblin/auth/views.py:79 msgid "Sorry, a user with that email address already exists." msgstr "" -#: mediagoblin/auth/views.py:180 +#: mediagoblin/auth/views.py:182 msgid "" "Your email address has been verified. You may now login, edit your profile, " "and submit images!" msgstr "" -#: mediagoblin/auth/views.py:186 +#: mediagoblin/auth/views.py:188 msgid "The verification key or user id is incorrect" msgstr "" -#: mediagoblin/auth/views.py:204 +#: mediagoblin/auth/views.py:206 msgid "You must be logged in so we know who to send the email to!" msgstr "" -#: mediagoblin/auth/views.py:212 +#: mediagoblin/auth/views.py:214 msgid "You've already verified your email address!" msgstr "" -#: mediagoblin/auth/views.py:225 +#: mediagoblin/auth/views.py:227 msgid "Resent your verification email." msgstr "" -#: mediagoblin/auth/views.py:260 +#: mediagoblin/auth/views.py:262 msgid "" "An email has been sent with instructions on how to change your password." msgstr "" -#: mediagoblin/auth/views.py:270 +#: mediagoblin/auth/views.py:272 msgid "" "Could not send password recovery email as your username is inactive or your " "account's email address has not been verified." msgstr "" -#: mediagoblin/auth/views.py:282 +#: mediagoblin/auth/views.py:284 msgid "Couldn't find someone with that username or email." msgstr "" -#: mediagoblin/auth/views.py:330 +#: mediagoblin/auth/views.py:332 msgid "You can now log in using your new password." msgstr "" @@ -127,6 +127,7 @@ msgid "" msgstr "" #: mediagoblin/edit/forms.py:44 mediagoblin/submit/forms.py:41 +#: mediagoblin/templates/mediagoblin/utils/license.html:20 msgid "License" msgstr "" @@ -150,27 +151,27 @@ msgstr "" msgid "New password" msgstr "" -#: mediagoblin/edit/views.py:68 +#: mediagoblin/edit/views.py:67 msgid "An entry with that slug already exists for this user." msgstr "" -#: mediagoblin/edit/views.py:92 +#: mediagoblin/edit/views.py:88 msgid "You are editing another user's media. Proceed with caution." msgstr "" -#: mediagoblin/edit/views.py:162 +#: mediagoblin/edit/views.py:158 msgid "You are editing a user's profile. Proceed with caution." msgstr "" -#: mediagoblin/edit/views.py:180 +#: mediagoblin/edit/views.py:174 msgid "Profile changes saved" msgstr "" -#: mediagoblin/edit/views.py:206 +#: mediagoblin/edit/views.py:200 msgid "Wrong password" msgstr "" -#: mediagoblin/edit/views.py:222 +#: mediagoblin/edit/views.py:216 msgid "Account settings saved" msgstr "" @@ -190,7 +191,7 @@ msgstr "" msgid "You must provide a file." msgstr "" -#: mediagoblin/submit/views.py:158 +#: mediagoblin/submit/views.py:156 msgid "Woohoo! Submitted!" msgstr "" @@ -238,7 +239,15 @@ msgstr "" #: mediagoblin/templates/mediagoblin/base.html:84 msgid "" "Powered by <a href=\"http://mediagoblin.org\">MediaGoblin</a>, a <a " -"href=\"http://gnu.org/\">GNU</a> project" +"href=\"http://gnu.org/\">GNU</a> project." +msgstr "" + +#: mediagoblin/templates/mediagoblin/base.html:87 +#, python-format +msgid "" +"Released under the <a " +"href=\"http://www.fsf.org/licensing/licenses/agpl-3.0.html\">AGPL</a>. <a " +"href=\"%(source_link)s\">Source code</a> available." msgstr "" #: mediagoblin/templates/mediagoblin/root.html:24 @@ -375,18 +384,18 @@ msgid "Media tagged with: %(tag_name)s" msgstr "" #: mediagoblin/templates/mediagoblin/media_displays/ascii.html:34 -#: mediagoblin/templates/mediagoblin/media_displays/video.html:46 +#: mediagoblin/templates/mediagoblin/media_displays/video.html:53 msgid "Original" msgstr "" -#: mediagoblin/templates/mediagoblin/media_displays/video.html:33 +#: mediagoblin/templates/mediagoblin/media_displays/video.html:40 msgid "" "Sorry, this video will not work because \n" "\t your web browser does not support HTML5 \n" "\t video." msgstr "" -#: mediagoblin/templates/mediagoblin/media_displays/video.html:36 +#: mediagoblin/templates/mediagoblin/media_displays/video.html:43 msgid "" "You can get a modern web browser that \n" "\t can play this video at <a href=\"http://getfirefox.com\">\n" @@ -411,55 +420,43 @@ msgstr "" msgid "<a href=\"%(user_url)s\">%(username)s</a>'s media" msgstr "" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:72 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:46 #, python-format -msgid "Added on %(date)s." +msgid "â– Browsing media by <a href=\"%(user_url)s\">%(username)s</a>" msgstr "" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:81 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:85 msgid "Edit" msgstr "" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:85 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:89 msgid "Delete" msgstr "" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:91 -#, python-format -msgid "%(comment_count)s comment" -msgstr "" - -#: mediagoblin/templates/mediagoblin/user_pages/media.html:93 -#, python-format -msgid "%(comment_count)s comments" -msgstr "" - -#: mediagoblin/templates/mediagoblin/user_pages/media.html:95 -msgid "No comments yet." -msgstr "" - -#: mediagoblin/templates/mediagoblin/user_pages/media.html:103 -msgid "Add one" +#: mediagoblin/templates/mediagoblin/user_pages/media.html:124 +msgid "Add a comment" msgstr "" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:112 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:133 msgid "" "You can use <a " "href=\"http://daringfireball.net/projects/markdown/basics\">Markdown</a> for" " formatting." msgstr "" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:116 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:137 msgid "Add this comment" msgstr "" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:138 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:159 msgid "at" msgstr "" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:153 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:174 #, python-format -msgid "<p>â– Browsing media by <a href=\"%(user_url)s\">%(username)s</a></p>" +msgid "" +"<h3>Added on</h3>\n" +" <p>%(date)s</p>" msgstr "" #: mediagoblin/templates/mediagoblin/user_pages/media_confirm_delete.html:30 @@ -468,7 +465,7 @@ msgid "Really delete %(title)s?" msgstr "" #: mediagoblin/templates/mediagoblin/user_pages/media_confirm_delete.html:50 -msgid "Delete Permanently" +msgid "Delete permanently" msgstr "" #: mediagoblin/templates/mediagoblin/user_pages/processing_panel.html:22 @@ -578,8 +575,13 @@ msgstr "" msgid "Atom feed" msgstr "" -#: mediagoblin/templates/mediagoblin/utils/license.html:21 -msgid "License:" +#: mediagoblin/templates/mediagoblin/utils/geolocation_map.html:25 +msgid "Location" +msgstr "" + +#: mediagoblin/templates/mediagoblin/utils/geolocation_map.html:38 +#, python-format +msgid "View on <a href=\"%(osm_url)s\">OpenStreetMap</a>" msgstr "" #: mediagoblin/templates/mediagoblin/utils/license.html:25 @@ -598,22 +600,18 @@ msgstr "" msgid "Go to page:" msgstr "" -#: mediagoblin/templates/mediagoblin/utils/prev_next.html:27 -#: mediagoblin/templates/mediagoblin/utils/prev_next.html:32 +#: mediagoblin/templates/mediagoblin/utils/prev_next.html:28 +#: mediagoblin/templates/mediagoblin/utils/prev_next.html:33 msgid "newer" msgstr "" -#: mediagoblin/templates/mediagoblin/utils/prev_next.html:38 -#: mediagoblin/templates/mediagoblin/utils/prev_next.html:43 +#: mediagoblin/templates/mediagoblin/utils/prev_next.html:39 +#: mediagoblin/templates/mediagoblin/utils/prev_next.html:44 msgid "older" msgstr "" #: mediagoblin/templates/mediagoblin/utils/tags.html:20 -msgid "View more media tagged with" -msgstr "" - -#: mediagoblin/templates/mediagoblin/utils/tags.html:25 -msgid "or" +msgid "Tagged with" msgstr "" #: mediagoblin/tools/exif.py:68 @@ -624,24 +622,22 @@ msgstr "" msgid "I am sure I want to delete this" msgstr "" -#: mediagoblin/user_pages/views.py:155 +#: mediagoblin/user_pages/views.py:153 msgid "Oops, your comment was empty." msgstr "" -#: mediagoblin/user_pages/views.py:161 +#: mediagoblin/user_pages/views.py:159 msgid "Your comment has been posted!" msgstr "" -#: mediagoblin/user_pages/views.py:183 +#: mediagoblin/user_pages/views.py:185 msgid "You deleted the media." msgstr "" -#: mediagoblin/user_pages/views.py:190 +#: mediagoblin/user_pages/views.py:192 msgid "The media was not deleted because you didn't check that you were sure." msgstr "" -#: mediagoblin/user_pages/views.py:198 +#: mediagoblin/user_pages/views.py:200 msgid "You are about to delete another user's media. Proceed with caution." msgstr "" - - diff --git a/mediagoblin/i18n/zh_TW/LC_MESSAGES/mediagoblin.mo b/mediagoblin/i18n/zh_TW/LC_MESSAGES/mediagoblin.mo Binary files differindex e907562e..cb7422bc 100644 --- a/mediagoblin/i18n/zh_TW/LC_MESSAGES/mediagoblin.mo +++ b/mediagoblin/i18n/zh_TW/LC_MESSAGES/mediagoblin.mo diff --git a/mediagoblin/i18n/zh_TW/LC_MESSAGES/mediagoblin.po b/mediagoblin/i18n/zh_TW/LC_MESSAGES/mediagoblin.po index 03571c01..809b214f 100644 --- a/mediagoblin/i18n/zh_TW/LC_MESSAGES/mediagoblin.po +++ b/mediagoblin/i18n/zh_TW/LC_MESSAGES/mediagoblin.po @@ -8,9 +8,9 @@ msgid "" msgstr "" "Project-Id-Version: GNU MediaGoblin\n" -"Report-Msgid-Bugs-To: http://bugs.foocorp.net/projects/mediagoblin/issues\n" -"POT-Creation-Date: 2012-01-29 13:31-0600\n" -"PO-Revision-Date: 2012-01-29 19:29+0000\n" +"Report-Msgid-Bugs-To: http://issues.mediagoblin.org/\n" +"POT-Creation-Date: 2012-03-18 15:16-0500\n" +"PO-Revision-Date: 2012-03-18 20:14+0000\n" "Last-Translator: cwebber <cwebber@dustycloud.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n" "MIME-Version: 1.0\n" @@ -20,7 +20,7 @@ msgstr "" "Language: zh_TW\n" "Plural-Forms: nplurals=1; plural=0\n" -#: mediagoblin/processing.py:143 +#: mediagoblin/processing.py:154 msgid "Invalid file given for media type." msgstr "指定錯誤的媒體類別ï¼" @@ -40,52 +40,52 @@ msgstr "é›»å郵件ä½ç½®" msgid "Sorry, registration is disabled on this instance." msgstr "抱æ‰, é€™å€‹é …ç›®å·²ç¶“è¢«æš«åœè¨»å†Š." -#: mediagoblin/auth/views.py:73 +#: mediagoblin/auth/views.py:75 msgid "Sorry, a user with that name already exists." msgstr "抱æ‰, 這個使用者å稱已經å˜åœ¨." -#: mediagoblin/auth/views.py:77 +#: mediagoblin/auth/views.py:79 msgid "Sorry, a user with that email address already exists." msgstr "抱æ‰ï¼Œæ¤é›»å郵件已被註冊了。" -#: mediagoblin/auth/views.py:180 +#: mediagoblin/auth/views.py:182 msgid "" "Your email address has been verified. You may now login, edit your profile, " "and submit images!" msgstr "ä½ çš„é›»å郵件ä½å€å·²è¢«èªè‰. ä½ ç¾åœ¨å°±å¯ä»¥ç™»å…¥, ç·¨è¼¯ä½ çš„å€‹äººæª”æ¡ˆè€Œä¸”éžäº¤ç…§ç‰‡!" -#: mediagoblin/auth/views.py:186 +#: mediagoblin/auth/views.py:188 msgid "The verification key or user id is incorrect" msgstr "èªè‰ç¢¼æˆ–是使用者帳號錯誤" -#: mediagoblin/auth/views.py:204 +#: mediagoblin/auth/views.py:206 msgid "You must be logged in so we know who to send the email to!" msgstr "ä½ å¿…é ˆç™»å…¥ï¼Œæˆ‘å€‘æ‰çŸ¥é“ä¿¡è¦é€çµ¦èª°ï¼" -#: mediagoblin/auth/views.py:212 +#: mediagoblin/auth/views.py:214 msgid "You've already verified your email address!" msgstr "ä½ çš„é›»å郵件已經確èªäº†ï¼" -#: mediagoblin/auth/views.py:225 +#: mediagoblin/auth/views.py:227 msgid "Resent your verification email." msgstr "é‡é€èªè‰ä¿¡." -#: mediagoblin/auth/views.py:260 +#: mediagoblin/auth/views.py:262 msgid "" "An email has been sent with instructions on how to change your password." msgstr "修改密碼的指示已經由電å郵件寄é€åˆ°ä½ 的信箱。" -#: mediagoblin/auth/views.py:270 +#: mediagoblin/auth/views.py:272 msgid "" "Could not send password recovery email as your username is inactive or your " "account's email address has not been verified." msgstr "無法傳é€å¯†ç¢¼å›žå¾©ä¿¡ä»¶ï¼Œå› ç‚ºä½ çš„ä½¿ç”¨è€…å稱已失效或是帳號尚未èªè‰ã€‚" -#: mediagoblin/auth/views.py:282 +#: mediagoblin/auth/views.py:284 msgid "Couldn't find someone with that username or email." msgstr "找ä¸åˆ°ç›¸é—œçš„使用者å稱或是電å郵件。" -#: mediagoblin/auth/views.py:330 +#: mediagoblin/auth/views.py:332 msgid "You can now log in using your new password." msgstr "ä½ ç¾åœ¨å¯ä»¥ç”¨æ–°çš„密碼登入了ï¼" @@ -103,10 +103,7 @@ msgid "" "You can use\n" " <a href=\"http://daringfireball.net/projects/markdown/basics\">\n" " Markdown</a> for formatting." -msgstr "" -"ä½ å¯ä»¥ç”¨\n" -" <a href=\"http://daringfireball.net/projects/markdown/basics\">\n" -" Markdown</a> 來排版." +msgstr "ä½ å¯ä»¥ç”¨\n <a href=\"http://daringfireball.net/projects/markdown/basics\">\n Markdown</a> 來排版." #: mediagoblin/edit/forms.py:33 mediagoblin/submit/forms.py:36 msgid "Tags" @@ -131,8 +128,9 @@ msgid "" msgstr "æ¤åª’體網å€çš„åç¨±ã€‚ä½ é€šå¸¸ä¸éœ€è¦è®Šå‹•這個的。" #: mediagoblin/edit/forms.py:44 mediagoblin/submit/forms.py:41 +#: mediagoblin/templates/mediagoblin/utils/license.html:20 msgid "License" -msgstr "" +msgstr "授權" #: mediagoblin/edit/forms.py:50 msgid "Bio" @@ -154,27 +152,27 @@ msgstr "è¼¸å…¥ä½ çš„èˆŠå¯†ç¢¼ä¾†è‰æ˜Žä½ æ“æœ‰é€™å€‹å¸³è™Ÿã€‚" msgid "New password" msgstr "新密碼" -#: mediagoblin/edit/views.py:68 +#: mediagoblin/edit/views.py:67 msgid "An entry with that slug already exists for this user." msgstr "這個自訂å—串已經被其他人用了" -#: mediagoblin/edit/views.py:92 +#: mediagoblin/edit/views.py:88 msgid "You are editing another user's media. Proceed with caution." msgstr "ä½ æ£åœ¨ç·¨è¼¯ä»–人的媒體檔案. 請謹慎處ç†." -#: mediagoblin/edit/views.py:162 +#: mediagoblin/edit/views.py:158 msgid "You are editing a user's profile. Proceed with caution." msgstr "ä½ æ£åœ¨ç·¨è¼¯ä¸€ä½ç”¨æˆ¶çš„æª”案. 請謹慎處ç†." -#: mediagoblin/edit/views.py:180 +#: mediagoblin/edit/views.py:174 msgid "Profile changes saved" msgstr "ä¿®æ”¹çš„æª”æ¡ˆå·²å˜æª”" -#: mediagoblin/edit/views.py:206 +#: mediagoblin/edit/views.py:200 msgid "Wrong password" msgstr "密碼錯誤" -#: mediagoblin/edit/views.py:222 +#: mediagoblin/edit/views.py:216 msgid "Account settings saved" msgstr "帳號è¨å®šå·²å˜æª”" @@ -194,7 +192,7 @@ msgstr "檔案" msgid "You must provide a file." msgstr "ä½ å¿…é ˆæä¾›ä¸€å€‹æª”案" -#: mediagoblin/submit/views.py:158 +#: mediagoblin/submit/views.py:156 msgid "Woohoo! Submitted!" msgstr "呼呼! é€å‡ºåŽ»åš•!" @@ -242,10 +240,16 @@ msgstr "登入" #: mediagoblin/templates/mediagoblin/base.html:84 msgid "" "Powered by <a href=\"http://mediagoblin.org\">MediaGoblin</a>, a <a " -"href=\"http://gnu.org/\">GNU</a> project" +"href=\"http://gnu.org/\">GNU</a> project." +msgstr "" + +#: mediagoblin/templates/mediagoblin/base.html:87 +#, python-format +msgid "" +"Released under the <a " +"href=\"http://www.fsf.org/licensing/licenses/agpl-3.0.html\">AGPL</a>. <a " +"href=\"%(source_link)s\">Source code</a> available." msgstr "" -"ç”± <a href=\"http://mediagoblin.org\">MediaGoblin</a>製作, 它是一個 <a " -"href=\"http://gnu.org/\">GNU</a> 專案" #: mediagoblin/templates/mediagoblin/root.html:24 msgid "Explore" @@ -259,9 +263,7 @@ msgstr "å˜¿ï¼æ¡è¿Žä¾†åˆ° 媒體怪ç¸(MediaGoblin) 網站" msgid "" "This site is running <a href=\"http://mediagoblin.org\">MediaGoblin</a>, an " "extraordinarily great piece of media hosting software." -msgstr "" -"æ¤ç¶²ç«™æ£é‹è¡Œ <a href=\"http://mediagoblin.org\">媒體怪ç¸(MediaGoblin)</a>, " -"他是一個超讚的媒體分享架站軟體." +msgstr "æ¤ç¶²ç«™æ£é‹è¡Œ <a href=\"http://mediagoblin.org\">媒體怪ç¸(MediaGoblin)</a>, 他是一個超讚的媒體分享架站軟體." #: mediagoblin/templates/mediagoblin/root.html:29 msgid "" @@ -279,10 +281,7 @@ msgid "" "<a class=\"button_action_highlight\" href=\"%(register_url)s\">Create an account at this site</a>\n" " or\n" " <a class=\"button_action\" href=\"http://wiki.mediagoblin.org/HackingHowto\">Set up MediaGoblin on your own server</a>" -msgstr "" -"<a class=\"button_action_highlight\" href=\"%(register_url)s\">在這網站建立帳號</a>\n" -" 或是\n" -" <a class=\"button_action\" href=\"http://wiki.mediagoblin.org/HackingHowto\">建立一個自己的媒體怪ç¸(MedaiGoblin)</a>" +msgstr "<a class=\"button_action_highlight\" href=\"%(register_url)s\">在這網站建立帳號</a>\n 或是\n <a class=\"button_action\" href=\"http://wiki.mediagoblin.org/HackingHowto\">建立一個自己的媒體怪ç¸(MedaiGoblin)</a>" #: mediagoblin/templates/mediagoblin/root.html:40 msgid "Most recent media" @@ -316,14 +315,7 @@ msgid "" "\n" "If you think this is an error, just ignore this email and continue being\n" "a happy goblin!" -msgstr "" -"å—¨ %(username)s,\n" -"\n" -"è¦æ›´æ”¹ GNU MediaGoblin的密碼,在ç€è¦½å™¨ä¸æ‰“開下é¢çš„ç¶²å€:\n" -"\n" -"%(verification_url)s\n" -"\n" -"å¦‚æžœä½ èªç‚ºé€™å€‹æ˜¯å€‹èª¤æœƒï¼Œè«‹å¿½ç•¥æ¤å°ä¿¡ä»¶ï¼Œç¹¼çºŒç•¶å€‹å¿«æ¨‚çš„goblin!" +msgstr "å—¨ %(username)s,\n\nè¦æ›´æ”¹ GNU MediaGoblin的密碼,在ç€è¦½å™¨ä¸æ‰“開下é¢çš„ç¶²å€:\n\n%(verification_url)s\n\nå¦‚æžœä½ èªç‚ºé€™å€‹æ˜¯å€‹èª¤æœƒï¼Œè«‹å¿½ç•¥æ¤å°ä¿¡ä»¶ï¼Œç¹¼çºŒç•¶å€‹å¿«æ¨‚çš„goblin!" #: mediagoblin/templates/mediagoblin/auth/login.html:30 msgid "Logging in failed!" @@ -358,12 +350,7 @@ msgid "" "your web browser:\n" "\n" "%(verification_url)s" -msgstr "" -"å—¨ %(username)s,\n" -"\n" -"啟動 GNU MediaGoblin 帳號, åœ¨ä½ çš„ç€è¦½å™¨ä¸æ‰“開下é¢çš„ç¶²å€:\n" -"\n" -"%(verification_url)s" +msgstr "å—¨ %(username)s,\n\n啟動 GNU MediaGoblin 帳號, åœ¨ä½ çš„ç€è¦½å™¨ä¸æ‰“開下é¢çš„ç¶²å€:\n\n%(verification_url)s" #: mediagoblin/templates/mediagoblin/edit/edit.html:29 #, python-format @@ -398,29 +385,23 @@ msgid "Media tagged with: %(tag_name)s" msgstr "æ¤åª’體被標è˜ç‚ºï¼š%(tag_name)s" #: mediagoblin/templates/mediagoblin/media_displays/ascii.html:34 -#: mediagoblin/templates/mediagoblin/media_displays/video.html:46 +#: mediagoblin/templates/mediagoblin/media_displays/video.html:53 msgid "Original" msgstr "原始的" -#: mediagoblin/templates/mediagoblin/media_displays/video.html:33 +#: mediagoblin/templates/mediagoblin/media_displays/video.html:40 msgid "" "Sorry, this video will not work because \n" "\t your web browser does not support HTML5 \n" "\t video." -msgstr "" -"抱æ‰, æ¤å½±ç‰‡ç„¡æ³•ä½¿ç”¨ï¼Œå› ç‚º \n" -"<span class=\"whitespace other\" title=\"Tab\">»</span> ä½ çš„ç€è¦½å™¨ä¸æ”¯æ´ HTML5 \n" -"<span class=\"whitespace other\" title=\"Tab\">»</span> 的影片." +msgstr "抱æ‰, æ¤å½±ç‰‡ç„¡æ³•ä½¿ç”¨ï¼Œå› ç‚º \n<span class=\"whitespace other\" title=\"Tab\">»</span> ä½ çš„ç€è¦½å™¨ä¸æ”¯æ´ HTML5 \n<span class=\"whitespace other\" title=\"Tab\">»</span> 的影片." -#: mediagoblin/templates/mediagoblin/media_displays/video.html:36 +#: mediagoblin/templates/mediagoblin/media_displays/video.html:43 msgid "" "You can get a modern web browser that \n" "\t can play this video at <a href=\"http://getfirefox.com\">\n" "\t http://getfirefox.com</a>!" -msgstr "" -"ä½ å¯ä»¥å–å¾—\n" -"<span class=\"whitespace other\" title=\"Tab\">»</span> æ’æ”¾é€™æ¨£æª”案的最新ç€è¦½å™¨ <a href=\"http://getfirefox.com\">\n" -"<span class=\"whitespace other\" title=\"Tab\">»</span> http://getfirefox.com</a>!" +msgstr "ä½ å¯ä»¥å–å¾—\n<span class=\"whitespace other\" title=\"Tab\">»</span> æ’æ”¾é€™æ¨£æª”案的最新ç€è¦½å™¨ <a href=\"http://getfirefox.com\">\n<span class=\"whitespace other\" title=\"Tab\">»</span> http://getfirefox.com</a>!" #: mediagoblin/templates/mediagoblin/submit/start.html:26 msgid "Add your media" @@ -440,56 +421,44 @@ msgstr "%(username)s的媒體" msgid "<a href=\"%(user_url)s\">%(username)s</a>'s media" msgstr "<a href=\"%(user_url)s\">%(username)s</a>的媒體檔案" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:72 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:46 #, python-format -msgid "Added on %(date)s." -msgstr "%(date)s. åŠ å…¥" +msgid "â– Browsing media by <a href=\"%(user_url)s\">%(username)s</a>" +msgstr "" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:81 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:85 msgid "Edit" msgstr "編輯" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:85 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:89 msgid "Delete" msgstr "刪除" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:91 -#, python-format -msgid "%(comment_count)s comment" -msgstr "%(comment_count)s è©•è«–" - -#: mediagoblin/templates/mediagoblin/user_pages/media.html:93 -#, python-format -msgid "%(comment_count)s comments" -msgstr "%(comment_count)s è©•è«–" - -#: mediagoblin/templates/mediagoblin/user_pages/media.html:95 -msgid "No comments yet." -msgstr "尚未有評論" - -#: mediagoblin/templates/mediagoblin/user_pages/media.html:103 -msgid "Add one" -msgstr "新增一個" +#: mediagoblin/templates/mediagoblin/user_pages/media.html:124 +msgid "Add a comment" +msgstr "" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:112 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:133 msgid "" "You can use <a " "href=\"http://daringfireball.net/projects/markdown/basics\">Markdown</a> for" " formatting." -msgstr "" +msgstr "ä½ å¯ä»¥ç”¨ <a href=\"http://daringfireball.net/projects/markdown/basics\"> Markdown</a> 來排版." -#: mediagoblin/templates/mediagoblin/user_pages/media.html:116 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:137 msgid "Add this comment" msgstr "å¢žåŠ æ¤è©•è«–" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:138 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:159 msgid "at" msgstr "在" -#: mediagoblin/templates/mediagoblin/user_pages/media.html:153 +#: mediagoblin/templates/mediagoblin/user_pages/media.html:174 #, python-format -msgid "<p>â– Browsing media by <a href=\"%(user_url)s\">%(username)s</a></p>" -msgstr "<p>■以%(username)sç€è¦½åª’體檔案 <a href=\"%(user_url)s\"></a></p>" +msgid "" +"<h3>Added on</h3>\n" +" <p>%(date)s</p>" +msgstr "" #: mediagoblin/templates/mediagoblin/user_pages/media_confirm_delete.html:30 #, python-format @@ -497,8 +466,8 @@ msgid "Really delete %(title)s?" msgstr "真的è¦åˆªé™¤ %(title)s?" #: mediagoblin/templates/mediagoblin/user_pages/media_confirm_delete.html:50 -msgid "Delete Permanently" -msgstr "æ°¸é 刪除" +msgid "Delete permanently" +msgstr "" #: mediagoblin/templates/mediagoblin/user_pages/processing_panel.html:22 msgid "Media processing panel" @@ -607,13 +576,18 @@ msgstr "feed圖示" msgid "Atom feed" msgstr "Atom feed" -#: mediagoblin/templates/mediagoblin/utils/license.html:21 -msgid "License:" +#: mediagoblin/templates/mediagoblin/utils/geolocation_map.html:25 +msgid "Location" +msgstr "" + +#: mediagoblin/templates/mediagoblin/utils/geolocation_map.html:38 +#, python-format +msgid "View on <a href=\"%(osm_url)s\">OpenStreetMap</a>" msgstr "" #: mediagoblin/templates/mediagoblin/utils/license.html:25 msgid "All rights reserved" -msgstr "" +msgstr "版權所有" #: mediagoblin/templates/mediagoblin/utils/pagination.html:39 msgid "↠Newer" @@ -627,50 +601,44 @@ msgstr "更舊 →" msgid "Go to page:" msgstr "è·³åˆ°é æ•¸ï¼š" -#: mediagoblin/templates/mediagoblin/utils/prev_next.html:27 -#: mediagoblin/templates/mediagoblin/utils/prev_next.html:32 +#: mediagoblin/templates/mediagoblin/utils/prev_next.html:28 +#: mediagoblin/templates/mediagoblin/utils/prev_next.html:33 msgid "newer" msgstr "æ›´æ–°" -#: mediagoblin/templates/mediagoblin/utils/prev_next.html:38 -#: mediagoblin/templates/mediagoblin/utils/prev_next.html:43 +#: mediagoblin/templates/mediagoblin/utils/prev_next.html:39 +#: mediagoblin/templates/mediagoblin/utils/prev_next.html:44 msgid "older" msgstr "更舊" #: mediagoblin/templates/mediagoblin/utils/tags.html:20 -msgid "View more media tagged with" -msgstr "看更多被標籤的媒體" - -#: mediagoblin/templates/mediagoblin/utils/tags.html:25 -msgid "or" -msgstr "或是" +msgid "Tagged with" +msgstr "" #: mediagoblin/tools/exif.py:68 msgid "Could not read the image file." -msgstr "" +msgstr "無法讀å–å½±åƒæª”案。" #: mediagoblin/user_pages/forms.py:30 msgid "I am sure I want to delete this" msgstr "我確定我想è¦åˆªé™¤" -#: mediagoblin/user_pages/views.py:155 +#: mediagoblin/user_pages/views.py:153 msgid "Oops, your comment was empty." msgstr "å•Šï¼Œä½ çš„ç•™è¨€æ˜¯ç©ºçš„ã€‚" -#: mediagoblin/user_pages/views.py:161 +#: mediagoblin/user_pages/views.py:159 msgid "Your comment has been posted!" msgstr "ä½ çš„ç•™è¨€å·²ç¶“åˆŠç™»ï¼" -#: mediagoblin/user_pages/views.py:183 +#: mediagoblin/user_pages/views.py:185 msgid "You deleted the media." msgstr "ä½ å·²åˆªé™¤æ¤åª’體檔案。" -#: mediagoblin/user_pages/views.py:190 +#: mediagoblin/user_pages/views.py:192 msgid "The media was not deleted because you didn't check that you were sure." msgstr "æ¤åª’é«”æª”æ¡ˆå°šæœªè¢«åˆªé™¤å› ç‚ºä½ é‚„æ²’æœ‰ç¢ºèªä½ 真的è¦åˆªé™¤ã€‚" -#: mediagoblin/user_pages/views.py:198 +#: mediagoblin/user_pages/views.py:200 msgid "You are about to delete another user's media. Proceed with caution." msgstr "ä½ åœ¨åˆªé™¤å…¶ä»–äººçš„åª’é«”æª”æ¡ˆã€‚è«‹å°å¿ƒè™•ç†å–”。" - - diff --git a/mediagoblin/init/__init__.py b/mediagoblin/init/__init__.py index 7ac59db1..1d8115cb 100644 --- a/mediagoblin/init/__init__.py +++ b/mediagoblin/init/__init__.py @@ -24,7 +24,7 @@ from mediagoblin.init.config import ( from mediagoblin import mg_globals from mediagoblin.mg_globals import setup_globals from mediagoblin.db.open import setup_connection_and_db_from_config, \ - check_db_migrations_current + check_db_migrations_current, load_models from mediagoblin.workbench import WorkbenchManager from mediagoblin.storage import storage_system_from_config @@ -56,6 +56,9 @@ def setup_global_and_app_config(config_path): def setup_database(): app_config = mg_globals.app_config + # Load all models for media types (plugins, ...) + load_models(app_config) + # Set up the database connection, db = setup_connection_and_db_from_config(app_config) diff --git a/mediagoblin/init/celery/__init__.py b/mediagoblin/init/celery/__init__.py index fb958909..67b020c3 100644 --- a/mediagoblin/init/celery/__init__.py +++ b/mediagoblin/init/celery/__init__.py @@ -18,7 +18,7 @@ import os import sys -MANDATORY_CELERY_IMPORTS = ['mediagoblin.processing'] +MANDATORY_CELERY_IMPORTS = ['mediagoblin.processing.task'] DEFAULT_SETTINGS_MODULE = 'mediagoblin.init.celery.dummy_settings_module' @@ -47,30 +47,12 @@ def setup_celery_from_config(app_config, global_config, celery_settings = {} - # set up mongodb stuff - celery_settings['CELERY_RESULT_BACKEND'] = 'mongodb' - if 'BROKER_BACKEND' not in celery_settings: - celery_settings['BROKER_BACKEND'] = 'mongodb' - - celery_mongo_settings = {} - - if 'db_host' in app_config: - celery_mongo_settings['host'] = app_config['db_host'] - if celery_settings['BROKER_BACKEND'] == 'mongodb': - celery_settings['BROKER_HOST'] = app_config['db_host'] - if 'db_port' in app_config: - celery_mongo_settings['port'] = app_config['db_port'] - if celery_settings['BROKER_BACKEND'] == 'mongodb': - celery_settings['BROKER_PORT'] = app_config['db_port'] - celery_mongo_settings['database'] = app_config['db_name'] - - celery_settings['CELERY_MONGODB_BACKEND_SETTINGS'] = celery_mongo_settings - - # Add anything else + # Add all celery settings from config for key, value in celery_conf.iteritems(): - key = key.upper() celery_settings[key] = value + # TODO: use default result stuff here if it exists + # add mandatory celery imports celery_imports = celery_settings.setdefault('CELERY_IMPORTS', []) celery_imports.extend(MANDATORY_CELERY_IMPORTS) diff --git a/mediagoblin/listings/views.py b/mediagoblin/listings/views.py index 48320cb2..9244293f 100644 --- a/mediagoblin/listings/views.py +++ b/mediagoblin/listings/views.py @@ -14,7 +14,7 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. -from mediagoblin.db.util import DESCENDING +from mediagoblin.db.util import media_entries_for_tag_slug, DESCENDING from mediagoblin.tools.pagination import Pagination from mediagoblin.tools.response import render_to_response @@ -29,11 +29,16 @@ def _get_tag_name_from_entries(media_entries, tag_slug): """ # ... this is slightly hacky looking :\ tag_name = tag_slug - if media_entries.count(): - for tag in media_entries[0]['tags']: + + for entry in media_entries: + for tag in entry.tags: if tag['slug'] == tag_slug: - tag_name == tag['name'] + tag_name = tag['name'] break + break + # TODO: Remove after SQL-switch, it's mongo specific + if hasattr(media_entries, "rewind"): + media_entries.rewind() return tag_name @@ -43,9 +48,7 @@ def tag_listing(request, page): """'Gallery'/listing for this tag slug""" tag_slug = request.matchdict[u'tag'] - cursor = request.db.MediaEntry.find( - {u'state': u'processed', - u'tags.slug': tag_slug}) + cursor = media_entries_for_tag_slug(request.db, tag_slug) cursor = cursor.sort('created', DESCENDING) pagination = Pagination(page, cursor) @@ -91,7 +94,7 @@ def tag_atom_feed(request): 'type': 'text/html'}]) for entry in cursor: feed.add(entry.get('title'), - entry.get('description_html'), + entry.description_html, id=entry.url_for_self(request.urlgen,qualified=True), content_type='html', author={'name': entry.get_uploader.username, diff --git a/mediagoblin/submit/security.py b/mediagoblin/media_types/ascii/migrations.py index 5dbc0db4..f54c23ea 100644 --- a/mediagoblin/submit/security.py +++ b/mediagoblin/media_types/ascii/migrations.py @@ -14,13 +14,4 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. -from mimetypes import guess_type - -ALLOWED = ['image/jpeg', 'image/png', 'image/tiff', 'image/gif'] - - -def check_filetype(posted_file): - if not guess_type(posted_file.filename)[0] in ALLOWED: - return False - - return True +MIGRATIONS = {} diff --git a/mediagoblin/media_types/ascii/models.py b/mediagoblin/media_types/ascii/models.py new file mode 100644 index 00000000..a35e6958 --- /dev/null +++ b/mediagoblin/media_types/ascii/models.py @@ -0,0 +1,34 @@ +# GNU MediaGoblin -- federated, autonomous media hosting +# Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + + +from mediagoblin.db.sql.base import Base + +from sqlalchemy import ( + Column, Integer, Unicode, UnicodeText, DateTime, Boolean, ForeignKey, + UniqueConstraint) + + +class AsciiData(Base): + __tablename__ = "ascii__mediadata" + + # The primary key *and* reference to the main media_entry + media_entry = Column(Integer, ForeignKey('core__media_entries.id'), + primary_key=True) + + +DATA_MODEL = AsciiData +MODELS = [AsciiData] diff --git a/mediagoblin/media_types/ascii/processing.py b/mediagoblin/media_types/ascii/processing.py index cd00b346..a2a52e9d 100644 --- a/mediagoblin/media_types/ascii/processing.py +++ b/mediagoblin/media_types/ascii/processing.py @@ -48,7 +48,7 @@ def process_ascii(entry): workbench.dir, 'conversions') os.mkdir(conversions_subdir) - queued_filepath = entry['queued_media_file'] + queued_filepath = entry.queued_media_file queued_filename = workbench.localized_file( mgg.queue_store, queued_filepath, 'source') @@ -117,7 +117,7 @@ def process_ascii(entry): 'xmlcharrefreplace')) mgg.queue_store.delete_file(queued_filepath) - entry['queued_media_file'] = [] + entry.queued_media_file = [] media_files_dict = entry.setdefault('media_files', {}) media_files_dict['thumb'] = thumb_filepath media_files_dict['unicode'] = unicode_filepath diff --git a/mediagoblin/media_types/image/migrations.py b/mediagoblin/media_types/image/migrations.py new file mode 100644 index 00000000..f54c23ea --- /dev/null +++ b/mediagoblin/media_types/image/migrations.py @@ -0,0 +1,17 @@ +# GNU MediaGoblin -- federated, autonomous media hosting +# Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +MIGRATIONS = {} diff --git a/mediagoblin/media_types/image/models.py b/mediagoblin/media_types/image/models.py new file mode 100644 index 00000000..5eb20ed4 --- /dev/null +++ b/mediagoblin/media_types/image/models.py @@ -0,0 +1,41 @@ +# GNU MediaGoblin -- federated, autonomous media hosting +# Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + + +from mediagoblin.db.sql.base import Base + +from sqlalchemy import ( + Column, Integer, Float, ForeignKey) +from mediagoblin.db.sql.extratypes import JSONEncoded + + +class ImageData(Base): + __tablename__ = "image__mediadata" + + # The primary key *and* reference to the main media_entry + media_entry = Column(Integer, ForeignKey('core__media_entries.id'), + primary_key=True) + width = Column(Integer) + height = Column(Integer) + exif_all = Column(JSONEncoded) + gps_longitude = Column(Float) + gps_latitude = Column(Float) + gps_altitude = Column(Float) + gps_direction = Column(Float) + + +DATA_MODEL = ImageData +MODELS = [ImageData] diff --git a/mediagoblin/media_types/image/processing.py b/mediagoblin/media_types/image/processing.py index bacfecb8..bbfcd32d 100644 --- a/mediagoblin/media_types/image/processing.py +++ b/mediagoblin/media_types/image/processing.py @@ -20,12 +20,40 @@ import logging from mediagoblin import mg_globals as mgg from mediagoblin.processing import BadMediaFail, \ - create_pub_filepath + create_pub_filepath, FilenameBuilder from mediagoblin.tools.exif import exif_fix_image_orientation, \ - extract_exif, clean_exif, get_gps_data, get_useful + extract_exif, clean_exif, get_gps_data, get_useful, \ + exif_image_needs_rotation _log = logging.getLogger(__name__) + +def resize_image(entry, filename, new_path, exif_tags, workdir, new_size, + size_limits=(0, 0)): + """Store a resized version of an image and return its pathname. + + Arguments: + entry -- the entry for the image to resize + filename -- the filename of the original image being resized + new_path -- public file path for the new resized image + exif_tags -- EXIF data for the original image + workdir -- directory path for storing converted image files + new_size -- 2-tuple size for the resized image + """ + try: + resized = Image.open(filename) + except IOError: + raise BadMediaFail() + resized = exif_fix_image_orientation(resized, exif_tags) # Fix orientation + resized.thumbnail(new_size, Image.ANTIALIAS) + + # Copy the new file to the conversion subdir, then remotely. + tmp_resized_filename = os.path.join(workdir, new_path[-1]) + with file(tmp_resized_filename, 'w') as resized_file: + resized.save(resized_file) + mgg.public_store.copy_local_to_storage(tmp_resized_filename, new_path) + + SUPPORTED_FILETYPES = ['png', 'gif', 'jpg', 'jpeg'] @@ -61,79 +89,48 @@ def process_image(entry): conversions_subdir = os.path.join( workbench.dir, 'conversions') os.mkdir(conversions_subdir) - queued_filepath = entry.queued_media_file queued_filename = workbench.localized_file( mgg.queue_store, queued_filepath, 'source') - - filename_bits = os.path.splitext(queued_filename) - basename = os.path.split(filename_bits[0])[1] - extension = filename_bits[1].lower() + name_builder = FilenameBuilder(queued_filename) # EXIF extraction exif_tags = extract_exif(queued_filename) gps_data = get_gps_data(exif_tags) - try: - thumb = Image.open(queued_filename) - except IOError: - raise BadMediaFail() - - thumb = exif_fix_image_orientation(thumb, exif_tags) - - thumb.thumbnail( - (mgg.global_config['media:thumb']['max_width'], - mgg.global_config['media:thumb']['max_height']), - Image.ANTIALIAS) - - # Copy the thumb to the conversion subdir, then remotely. - thumb_filename = 'thumbnail' + extension - thumb_filepath = create_pub_filepath(entry, thumb_filename) - - tmp_thumb_filename = os.path.join( - conversions_subdir, thumb_filename) - - with file(tmp_thumb_filename, 'w') as thumb_file: - thumb.save(thumb_file) - - mgg.public_store.copy_local_to_storage( - tmp_thumb_filename, thumb_filepath) + # Always create a small thumbnail + thumb_filepath = create_pub_filepath( + entry, name_builder.fill('{basename}.thumbnail{ext}')) + resize_image(entry, queued_filename, thumb_filepath, + exif_tags, conversions_subdir, + (mgg.global_config['media:thumb']['max_width'], + mgg.global_config['media:thumb']['max_height'])) # If the size of the original file exceeds the specified size of a `medium` - # file, a `medium.jpg` files is created and later associated with the media + # file, a `.medium.jpg` files is created and later associated with the media # entry. medium = Image.open(queued_filename) - - # Fix orientation - medium = exif_fix_image_orientation(medium, exif_tags) - if medium.size[0] > mgg.global_config['media:medium']['max_width'] \ - or medium.size[1] > mgg.global_config['media:medium']['max_height']: - medium.thumbnail( + or medium.size[1] > mgg.global_config['media:medium']['max_height'] \ + or exif_image_needs_rotation(exif_tags): + medium_filepath = create_pub_filepath( + entry, name_builder.fill('{basename}.medium{ext}')) + resize_image( + entry, queued_filename, medium_filepath, + exif_tags, conversions_subdir, (mgg.global_config['media:medium']['max_width'], - mgg.global_config['media:medium']['max_height']), - Image.ANTIALIAS) - - medium_filename = 'medium' + extension - medium_filepath = create_pub_filepath(entry, medium_filename) - - tmp_medium_filename = os.path.join( - conversions_subdir, medium_filename) - - with file(tmp_medium_filename, 'w') as medium_file: - medium.save(medium_file) - - mgg.public_store.copy_local_to_storage( - tmp_medium_filename, medium_filepath) + mgg.global_config['media:medium']['max_height'])) + else: + medium_filepath = None # we have to re-read because unlike PIL, not everything reads # things in string representation :) queued_file = file(queued_filename, 'rb') with queued_file: - #create_pub_filepath(entry, queued_filepath[-1]) - original_filepath = create_pub_filepath(entry, basename + extension) + original_filepath = create_pub_filepath( + entry, name_builder.fill('{basename}{ext}')) with mgg.public_store.get_file(original_filepath, 'wb') \ as original_file: @@ -147,15 +144,19 @@ def process_image(entry): media_files_dict = entry.setdefault('media_files', {}) media_files_dict['thumb'] = thumb_filepath media_files_dict['original'] = original_filepath - media_files_dict['medium'] = medium_filepath + if medium_filepath: + media_files_dict['medium'] = medium_filepath # Insert exif data into database - media_data = entry.setdefault('media_data', {}) - media_data['exif'] = { - 'clean': clean_exif(exif_tags)} - media_data['exif']['useful'] = get_useful( - media_data['exif']['clean']) - media_data['gps'] = gps_data + exif_all = clean_exif(exif_tags) + + if len(exif_all): + entry.media_data_init(exif_all=exif_all) + + if len(gps_data): + for key in list(gps_data.keys()): + gps_data['gps_' + key] = gps_data.pop(key) + entry.media_data_init(**gps_data) # clean up workbench workbench.destroy_self() diff --git a/mediagoblin/media_types/video/__init__.py b/mediagoblin/media_types/video/__init__.py index a53927d5..3faa5b9f 100644 --- a/mediagoblin/media_types/video/__init__.py +++ b/mediagoblin/media_types/video/__init__.py @@ -20,10 +20,10 @@ from mediagoblin.media_types.video.processing import process_video, \ MEDIA_MANAGER = { "human_readable": "Video", - "processor": process_video, # alternately a string, - # 'mediagoblin.media_types.image.processing'? + "processor": process_video, # alternately a string, + # 'mediagoblin.media_types.image.processing'? "sniff_handler": sniff_handler, "display_template": "mediagoblin/media_displays/video.html", "default_thumb": "images/media_thumbs/video.jpg", "accepted_extensions": [ - "mp4", "mov", "webm", "avi", "3gp", "3gpp", "mkv", "ogv"]} + "mp4", "mov", "webm", "avi", "3gp", "3gpp", "mkv", "ogv", "m4v"]} diff --git a/mediagoblin/media_types/video/migrations.py b/mediagoblin/media_types/video/migrations.py new file mode 100644 index 00000000..f54c23ea --- /dev/null +++ b/mediagoblin/media_types/video/migrations.py @@ -0,0 +1,17 @@ +# GNU MediaGoblin -- federated, autonomous media hosting +# Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +MIGRATIONS = {} diff --git a/mediagoblin/media_types/video/models.py b/mediagoblin/media_types/video/models.py new file mode 100644 index 00000000..cf42b7c8 --- /dev/null +++ b/mediagoblin/media_types/video/models.py @@ -0,0 +1,35 @@ +# GNU MediaGoblin -- federated, autonomous media hosting +# Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + + +from mediagoblin.db.sql.base import Base + +from sqlalchemy import ( + Column, Integer, SmallInteger, ForeignKey) + + +class VideoData(Base): + __tablename__ = "video__mediadata" + + # The primary key *and* reference to the main media_entry + media_entry = Column(Integer, ForeignKey('core__media_entries.id'), + primary_key=True) + width = Column(SmallInteger) + height = Column(SmallInteger) + + +DATA_MODEL = VideoData +MODELS = [VideoData] diff --git a/mediagoblin/media_types/video/processing.py b/mediagoblin/media_types/video/processing.py index 4c44e65f..6a5ce364 100644 --- a/mediagoblin/media_types/video/processing.py +++ b/mediagoblin/media_types/video/processing.py @@ -19,7 +19,8 @@ import logging import os from mediagoblin import mg_globals as mgg -from mediagoblin.processing import create_pub_filepath +from mediagoblin.processing import mark_entry_failed, \ + create_pub_filepath, FilenameBuilder from . import transcoders logging.basicConfig() @@ -58,16 +59,13 @@ def process_video(entry): queued_filename = workbench.localized_file( mgg.queue_store, queued_filepath, 'source') + name_builder = FilenameBuilder(queued_filename) medium_filepath = create_pub_filepath( - entry, - '{original}-640p.webm'.format( - original=os.path.splitext( - queued_filepath[-1])[0] # Select the file name without .ext - )) + entry, name_builder.fill('{basename}-640p.webm')) thumbnail_filepath = create_pub_filepath( - entry, 'thumbnail.jpg') + entry, name_builder.fill('{basename}.thumbnail.jpg')) # Create a temporary file for the video destination tmp_dst = tempfile.NamedTemporaryFile() @@ -86,9 +84,9 @@ def process_video(entry): entry.media_files['webm_640'] = medium_filepath # Save the width and height of the transcoded video - entry.media_data['video'] = { - u'width': transcoder.dst_data.videowidth, - u'height': transcoder.dst_data.videoheight} + entry.media_data_init( + width=transcoder.dst_data.videowidth, + height=transcoder.dst_data.videoheight) # Create a temporary file for the video thumbnail tmp_thumb = tempfile.NamedTemporaryFile() diff --git a/mediagoblin/processing.py b/mediagoblin/processing/__init__.py index 989591de..4a827af4 100644 --- a/mediagoblin/processing.py +++ b/mediagoblin/processing/__init__.py @@ -15,16 +15,13 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. import logging +import os -from celery.task import Task - -from mediagoblin.db.util import ObjectId +from mediagoblin.db.util import atomic_update from mediagoblin import mg_globals as mgg from mediagoblin.tools.translate import lazy_pass_to_ugettext as _ -from mediagoblin.media_types import get_media_manager - _log = logging.getLogger(__name__) @@ -34,57 +31,36 @@ def create_pub_filepath(entry, filename): unicode(entry._id), filename]) +class FilenameBuilder(object): + """Easily slice and dice filenames. -################################ -# Media processing initial steps -################################ + Initialize this class with an original file path, then use the fill() + method to create new filenames based on the original. -class ProcessMedia(Task): """ - DEPRECATED -- This now resides in the individual media plugins + MAX_FILENAME_LENGTH = 255 # VFAT's maximum filename length - Pass this entry off for processing. - """ - def run(self, media_id): - """ - Pass the media entry off to the appropriate processing function - (for now just process_image...) - """ - entry = mgg.database.MediaEntry.one( - {'_id': ObjectId(media_id)}) - - # Try to process, and handle expected errors. - try: - #__import__(entry.media_type) - manager = get_media_manager(entry.media_type) - _log.debug('Processing {0}'.format(entry)) - manager['processor'](entry) - except BaseProcessingFail, exc: - mark_entry_failed(entry._id, exc) - return - except ImportError, exc: - _log.error( - 'Entry {0} failed to process due to an import error: {1}'\ - .format( - entry.title, - exc)) - - mark_entry_failed(entry._id, exc) - - entry.state = u'processed' - entry.save() - - def on_failure(self, exc, task_id, args, kwargs, einfo): - """ - If the processing failed we should mark that in the database. + def __init__(self, path): + """Initialize a builder from an original file path.""" + self.dirpath, self.basename = os.path.split(path) + self.basename, self.ext = os.path.splitext(self.basename) + self.ext = self.ext.lower() + + def fill(self, fmtstr): + """Build a new filename based on the original. + + The fmtstr argument can include the following: + {basename} -- the original basename, with the extension removed + {ext} -- the original extension, always lowercase + + If necessary, {basename} will be truncated so the filename does not + exceed this class' MAX_FILENAME_LENGTH in length. - Assuming that the exception raised is a subclass of - BaseProcessingFail, we can use that to get more information - about the failure and store that for conveying information to - users about the failure, etc. """ - entry_id = args[0] - mark_entry_failed(entry_id, exc) + basename_len = (self.MAX_FILENAME_LENGTH - + len(fmtstr.format(basename='', ext=self.ext))) + return fmtstr.format(basename=self.basename[:basename_len], + ext=self.ext) def mark_entry_failed(entry_id, exc): @@ -105,21 +81,22 @@ def mark_entry_failed(entry_id, exc): if isinstance(exc, BaseProcessingFail): # Looks like yes, so record information about that failure and any # metadata the user might have supplied. - mgg.database['media_entries'].update( + atomic_update(mgg.database.MediaEntry, {'_id': entry_id}, - {'$set': {u'state': u'failed', - u'fail_error': exc.exception_path, - u'fail_metadata': exc.metadata}}) + {u'state': u'failed', + u'fail_error': exc.exception_path, + u'fail_metadata': exc.metadata}) else: + _log.warn("No idea what happened here, but it failed: %r", exc) # Looks like no, so just mark it as failed and don't record a # failure_error (we'll assume it wasn't handled) and don't record # metadata (in fact overwrite it if somehow it had previous info # here) - mgg.database['media_entries'].update( + atomic_update(mgg.database.MediaEntry, {'_id': entry_id}, - {'$set': {u'state': u'failed', - u'fail_error': None, - u'fail_metadata': {}}}) + {u'state': u'failed', + u'fail_error': None, + u'fail_metadata': {}}) class BaseProcessingFail(Exception): diff --git a/mediagoblin/processing/task.py b/mediagoblin/processing/task.py new file mode 100644 index 00000000..901d293b --- /dev/null +++ b/mediagoblin/processing/task.py @@ -0,0 +1,78 @@ +# GNU MediaGoblin -- federated, autonomous media hosting +# Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +import logging + +from celery.task import Task + +from mediagoblin import mg_globals as mgg +from mediagoblin.db.util import ObjectId +from mediagoblin.media_types import get_media_manager +from mediagoblin.processing import mark_entry_failed, BaseProcessingFail + +_log = logging.getLogger(__name__) + + +################################ +# Media processing initial steps +################################ + +class ProcessMedia(Task): + """ + DEPRECATED -- This now resides in the individual media plugins + + Pass this entry off for processing. + """ + def run(self, media_id): + """ + Pass the media entry off to the appropriate processing function + (for now just process_image...) + """ + entry = mgg.database.MediaEntry.one( + {'_id': ObjectId(media_id)}) + + # Try to process, and handle expected errors. + try: + #__import__(entry.media_type) + manager = get_media_manager(entry.media_type) + _log.debug('Processing {0}'.format(entry)) + manager['processor'](entry) + except BaseProcessingFail, exc: + mark_entry_failed(entry._id, exc) + return + except ImportError, exc: + _log.error( + 'Entry {0} failed to process due to an import error: {1}'\ + .format( + entry.title, + exc)) + + mark_entry_failed(entry._id, exc) + + entry.state = u'processed' + entry.save() + + def on_failure(self, exc, task_id, args, kwargs, einfo): + """ + If the processing failed we should mark that in the database. + + Assuming that the exception raised is a subclass of + BaseProcessingFail, we can use that to get more information + about the failure and store that for conveying information to + users about the failure, etc. + """ + entry_id = args[0] + mark_entry_failed(entry_id, exc) diff --git a/mediagoblin/static/css/base.css b/mediagoblin/static/css/base.css index efd7b561..34be4f16 100644 --- a/mediagoblin/static/css/base.css +++ b/mediagoblin/static/css/base.css @@ -29,7 +29,7 @@ body { background-color: #111; background-image: url("../images/background.png"); color: #C3C3C3; - padding: none; + padding: 0; margin: 0px; height: 100%; font: 16px 'Lato', 'Helvetica Neue', Arial, 'Liberation Sans', FreeSans, sans-serif; @@ -79,6 +79,22 @@ a.highlight { color: #fff; } +em { + font-style: italic; +} + +strong { + font-weight: bold; +} + +ul { + list-style: disc inside; +} + +ol { + list-style: decimal inside; +} + label { font-weight: normal; } @@ -97,21 +113,21 @@ input, textarea { } header { - width: 100%; - height: 36px; - padding-top: 14px; + width: 98%; + padding: 6px 1% 0; margin-bottom: 20px; - border-bottom: 1px solid #333; + background-color: #222; } .header_right { float: right; + margin: 8px 0px 8px 8px; } a.logo { color: #fff; font-weight: bold; - margin-right: 8px; + margin: 8px 8px 8px 0; } .logo img { @@ -128,7 +144,7 @@ footer { height: 30px; border-top: 1px solid #333; bottom: 0px; - padding-top: 8px; + padding: 8px 0; text-align: center; font-size: 0.875em; clear: both; @@ -144,7 +160,6 @@ footer { .media_sidebar { width: 280px; margin-left: 10px; - margin-right: 0px; float: left; } @@ -162,7 +177,7 @@ footer { /* common website elements */ -.button_action, .button_action_highlight { +.button_action, .button_action_highlight, .button_form { display: inline-block; color: #c3c3c3; background-color: #363636; @@ -177,43 +192,17 @@ footer { cursor: pointer; } -.button_action_highlight { +.button_action_highlight, .button_form { background-color: #86D4B1; border-color: #A2DEC3 #6CAA8E #5C9179; color: #283F35; } -.button_form, .cancel_link { - height: 32px; +.button_form { min-width: 99px; - background-color: #86d4b1; - background-image: -webkit-gradient(linear, left top, left bottom, from(#86d4b1), to(#62caa2)); - background-image: -webkit-linear-gradient(top, #86d4b1, #62caa2); - background-image: -moz-linear-gradient(top, #86d4b1, #62caa2); - background-image: -ms-linear-gradient(top, #86d4b1, #62caa2); - background-image: -o-linear-gradient(top, #86d4b1, #62caa2); - background-image: linear-gradient(top, #86d4b1, #62caa2); - box-shadow: 0px 0px 4px #000; - border-radius: 3px; - border: none; - color: #272727; margin: 10px 0px 10px 15px; text-align: center; - padding-left: 11px; - padding-right: 11px; - text-decoration: none; font-family: 'Lato', sans-serif; - font-weight: bold; -} - -.cancel_link { - background-color: #aaa; - background-image: -webkit-gradient(linear, left top, left bottom, from(##D2D2D2), to(#aaa)); - background-image: -webkit-linear-gradient(top, #D2D2D2, #aaa); - background-image: -moz-linear-gradient(top, #D2D2D2, #aaa); - background-image: -ms-linear-gradient(top, #D2D2D2, #aaa); - background-image: -o-linear-gradient(top, #D2D2D2, #aaa); - background-image: linear-gradient(top, #D2D2D2, #aaa); } .pagination { @@ -236,15 +225,6 @@ text-align: center; float: right; } -textarea#comment_content { - resize: vertical; - width: 634px; - height: 90px; - border: none; - background-color: #f1f1f1; - padding: 3px; -} - .clear { clear: both; display: block; @@ -254,12 +234,21 @@ textarea#comment_content { height: 0; } +.media_sidebar h3 { + font-size: 1em; + margin: 0 0 5px; + border: none; +} + +.media_sidebar p { + padding-left: 8px; +} + /* forms */ .form_box,.form_box_xl { background-color: #222; - background-image: url("../images/background_lines.png"); - background-repeat: repeat-x; + border-top: 6px solid #D49086; padding: 3% 5%; display: block; float: none; @@ -274,7 +263,7 @@ textarea#comment_content { } .edit_box { - background-image: url("../images/background_edit.png"); + border-top: 6px dashed #D49086 } .form_field_input input, .form_field_input textarea { @@ -336,23 +325,32 @@ textarea#description, textarea#bio { } textarea#comment_content { - width: 634px; + resize: vertical; + width: 100%; height: 90px; border: none; background-color: #f1f1f1; padding: 3px; } +#form_comment .form_field_input { + padding-right: 6px; +} + /* media galleries */ .media_thumbnail { + float: left; padding: 0px; width: 180px; + height: 156px; overflow: hidden; - float: left; - margin: 0px 4px 10px 4px; + margin: 0px 4px 10px; text-align: center; font-size: 0.875em; + background-color: #222; + border-radius: 0 0 5px 5px; + padding: 0 0 6px; } .media_thumbnail a { @@ -360,10 +358,20 @@ textarea#comment_content { text-decoration: none; } +.media_thumbnail img { + max-height: 135px; +} + /* media detail */ h2.media_title { margin-bottom: 0px; + display: inline-block; +} + +p.context { + display: inline-block; + padding-top: 4px; } p.media_specs { @@ -390,19 +398,21 @@ img.media_icon { /* navigation */ +.navigation { + float: right; +} + .navigation_button { width: 135px; - display: block; - float: left; + display: inline-block; text-align: center; background-color: #1d1d1d; border: 1px solid; border-color: #2c2c2c #232323 #1a1a1a; border-radius: 4px; text-decoration: none; - padding: 12px 0 16px; - font-size: 1.4em; - margin: 0 0 20px + padding: 4px 0 8px; + margin: 0 0 6px; } .navigation_left { @@ -487,23 +497,82 @@ table.media_panel th { } /* Media queries and other responsivisivity */ -@media screen and (max-width: 680px) { +@media screen and (max-width: 940px) { .media_pane { width: 100%; margin: 0px; } + + .media_sidebar { + width: 100%; + margin: 0px; + } + img.media_image { width: 100%; + display: inline; + } + + .media_thumbnail { + width: 21%; } -} -@media screen and (max-width: 960px) { .profile_sidebar { width: 100%; margin: 0px; } + .profile_showcase { width: 100%; margin: 0px; } + + .navigation { + float: none; + } + + .navigation_button { + width: 49%; + float: right; + } + + .navigation_left { + margin-right: 0; + float: left; + } + + .navigation { + float: none; + } + + .navigation_button { + width: 49%; + float: right; + padding: 10px 0 14px; + } + + .navigation_left { + margin-right: 0; + float: left; + } + + .button_action, .button_action_highlight, .button_form { + padding: 9px 14px; + } + + header { + text-align: center; + } +} + +@media screen and (max-width: 570px) { + .media_thumbnail { + width: 29%; + } +} + +@media screen and (max-width: 380px) { + .media_thumbnail { + width: 46%; + } } diff --git a/mediagoblin/static/images/background_edit.png b/mediagoblin/static/images/background_edit.png Binary files differdeleted file mode 100644 index 4071fd53..00000000 --- a/mediagoblin/static/images/background_edit.png +++ /dev/null diff --git a/mediagoblin/static/images/background_lines.png b/mediagoblin/static/images/background_lines.png Binary files differdeleted file mode 100644 index e1b07afe..00000000 --- a/mediagoblin/static/images/background_lines.png +++ /dev/null diff --git a/mediagoblin/static/images/goblin.ico b/mediagoblin/static/images/goblin.ico Binary files differindex f2e7152f..ae5a1b12 100644 --- a/mediagoblin/static/images/goblin.ico +++ b/mediagoblin/static/images/goblin.ico diff --git a/mediagoblin/static/images/goblin.png b/mediagoblin/static/images/goblin.png Binary files differindex 0a3ad22e..672ed61a 100644 --- a/mediagoblin/static/images/goblin.png +++ b/mediagoblin/static/images/goblin.png diff --git a/mediagoblin/static/js/audio.js b/mediagoblin/static/js/audio.js index 8c91bd0b..91d52f96 100644 --- a/mediagoblin/static/js/audio.js +++ b/mediagoblin/static/js/audio.js @@ -133,6 +133,12 @@ var audioPlayer = new Object(); })(audioPlayer); $(document).ready(function () { + if (!$('.audio-media').length) { + return; + } + + console.log('Initializing audio player'); + audioElements = $('.audio-media .audio-player'); audioPlayer.init(audioElements[0]); audioPlayer.attachToImage($('.audio-spectrogram img')[0]); diff --git a/mediagoblin/static/js/autofilledin_password.js b/mediagoblin/static/js/autofilledin_password.js new file mode 100644 index 00000000..45e867fe --- /dev/null +++ b/mediagoblin/static/js/autofilledin_password.js @@ -0,0 +1,25 @@ +/** + * GNU MediaGoblin -- federated, autonomous media hosting + * Copyright (C) 2012 MediaGoblin contributors. See AUTHORS. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +$(document).ready(function(){ + $('#forgot_password').click(function(event){ + event.preventDefault(); + window.location.pathname = $(this).attr('href') + '?username=' + + $('#username').val(); + }); +}); diff --git a/mediagoblin/static/js/extlib/video-js b/mediagoblin/static/js/extlib/video-js new file mode 120000 index 00000000..35da21ca --- /dev/null +++ b/mediagoblin/static/js/extlib/video-js @@ -0,0 +1 @@ +../../../../extlib/video-js
\ No newline at end of file diff --git a/mediagoblin/static/js/geolocation-map.js b/mediagoblin/static/js/geolocation-map.js index a2c62045..de49a37d 100644 --- a/mediagoblin/static/js/geolocation-map.js +++ b/mediagoblin/static/js/geolocation-map.js @@ -17,6 +17,11 @@ */ $(document).ready(function () { + if (!$('#tile-map').length) { + return; + } + console.log('Initializing map'); + var longitude = Number( $('#tile-map #gps-longitude').val()); var latitude = Number( diff --git a/mediagoblin/static/js/keyboard_navigation.js b/mediagoblin/static/js/keyboard_navigation.js new file mode 100644 index 00000000..7401e4d8 --- /dev/null +++ b/mediagoblin/static/js/keyboard_navigation.js @@ -0,0 +1,41 @@ +/** + * GNU MediaGoblin -- federated, autonomous media hosting + * Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/* It must be wrapped into a function and you also cannot use + * $(':not(textarea, input)') because of some reason. */ + +$(document).ready(function(){ + $('textarea, input').keydown(function(event){ + event.stopPropagation(); + }); +}); + +$(document).keydown(function(event){ + switch(event.which){ + case 37: + if($('a.navigation_left').length) { + window.location = $('a.navigation_left').attr('href'); + } + break; + case 39: + if($('a.navigation_right').length) { + window.location = $('a.navigation_right').attr('href'); + } + break; + } +}); diff --git a/mediagoblin/static/js/show_password.js b/mediagoblin/static/js/show_password.js index e42d44ea..b3fbc862 100644 --- a/mediagoblin/static/js/show_password.js +++ b/mediagoblin/static/js/show_password.js @@ -17,6 +17,7 @@ */ $(document).ready(function(){ + //Create a duplicate password field. We could change the input type dynamically, but this angers the IE gods (not just IE6). $("#password").after('<input type="text" value="" name="password_clear" id="password_clear" /><label><input type="checkbox" id="password_boolean" />Show password</label>'); $('#password_clear').hide(); $('#password_boolean').click(function(){ diff --git a/mediagoblin/submit/views.py b/mediagoblin/submit/views.py index 4fafe1e3..517fb646 100644 --- a/mediagoblin/submit/views.py +++ b/mediagoblin/submit/views.py @@ -20,7 +20,8 @@ from os.path import splitext from cgi import FieldStorage from celery import registry -import urllib,urllib2 +import urllib +import urllib2 import logging _log = logging.getLogger(__name__) @@ -28,12 +29,13 @@ _log = logging.getLogger(__name__) from werkzeug.utils import secure_filename from mediagoblin.db.util import ObjectId -from mediagoblin.tools.text import cleaned_markdown_conversion, convert_to_tag_list_of_dicts +from mediagoblin.tools.text import convert_to_tag_list_of_dicts from mediagoblin.tools.translate import pass_to_ugettext as _ from mediagoblin.tools.response import render_to_response, redirect from mediagoblin.decorators import require_active_login -from mediagoblin.submit import forms as submit_forms, security -from mediagoblin.processing import mark_entry_failed, ProcessMedia +from mediagoblin.submit import forms as submit_forms +from mediagoblin.processing import mark_entry_failed +from mediagoblin.processing.task import ProcessMedia from mediagoblin.messages import add_message, SUCCESS from mediagoblin.media_types import sniff_media, \ InvalidFileType, FileTypeNotSupported @@ -63,15 +65,13 @@ def submit_start(request): # create entry and save in database entry = request.db.MediaEntry() - entry['_id'] = ObjectId() + entry.id = ObjectId() entry.media_type = unicode(media_type) entry.title = ( unicode(request.POST['title']) or unicode(splitext(filename)[0])) entry.description = unicode(request.POST.get('description')) - entry.description_html = cleaned_markdown_conversion( - entry.description) entry.license = unicode(request.POST.get('license', "")) or None @@ -84,11 +84,18 @@ def submit_start(request): # Generate a slug from the title entry.generate_slug() + # We generate this ourselves so we know what the taks id is for + # retrieval later. + + # (If we got it off the task's auto-generation, there'd be + # a risk of a race condition when we'd save after sending + # off the task) + task_id = unicode(uuid.uuid4()) # Now store generate the queueing related filename queue_filepath = request.app.queue_store.get_unique_filepath( ['media_entries', - unicode(entry._id), + task_id, secure_filename(filename)]) # queue appropriately @@ -101,14 +108,7 @@ def submit_start(request): # Add queued filename to the entry entry.queued_media_file = queue_filepath - # We generate this ourselves so we know what the taks id is for - # retrieval later. - - # (If we got it off the task's auto-generation, there'd be - # a risk of a race condition when we'd save after sending - # off the task) - task_id = unicode(uuid.uuid4()) - entry['queued_task_id'] = task_id + entry.queued_task_id = task_id # Save now so we have this data before kicking off processing entry.save(validate=True) @@ -136,9 +136,10 @@ def submit_start(request): raise if mg_globals.app_config["push_urls"]: - feed_url=request.urlgen( + feed_url = request.urlgen( 'mediagoblin.user_pages.atom_feed', - qualified=True,user=request.user.username) + qualified=True, + user=request.user.username) hubparameters = { 'hub.mode': 'publish', 'hub.url': feed_url} @@ -165,7 +166,7 @@ def submit_start(request): user=request.user.username) except Exception as e: ''' - This section is intended to catch exceptions raised in + This section is intended to catch exceptions raised in mediagobling.media_types ''' if isinstance(e, InvalidFileType) or \ diff --git a/mediagoblin/templates/mediagoblin/auth/login.html b/mediagoblin/templates/mediagoblin/auth/login.html index 39f07d33..8161ea9d 100644 --- a/mediagoblin/templates/mediagoblin/auth/login.html +++ b/mediagoblin/templates/mediagoblin/auth/login.html @@ -19,6 +19,11 @@ {% import "/mediagoblin/utils/wtforms.html" as wtforms_util %} +{% block mediagoblin_head %} + <script type="text/javascript" + src="{{ request.staticdirect('/js/autofilledin_password.js') }}"></script> +{% endblock %} + {% block mediagoblin_content %} <form action="{{ request.urlgen('mediagoblin.auth.login') }}" method="POST" enctype="multipart/form-data"> @@ -38,7 +43,7 @@ {% endif %} {{ wtforms_util.render_divs(login_form) }} <p> - <a href="{{ request.urlgen('mediagoblin.auth.forgot_password') }}"> + <a href="{{ request.urlgen('mediagoblin.auth.forgot_password') }}" id="forgot_password"> {% trans %}Forgot your password?{% endtrans %}</a> </p> <div class="form_submit_buttons"> diff --git a/mediagoblin/templates/mediagoblin/base.html b/mediagoblin/templates/mediagoblin/base.html index f3ebc3fa..c2d5457d 100644 --- a/mediagoblin/templates/mediagoblin/base.html +++ b/mediagoblin/templates/mediagoblin/base.html @@ -19,7 +19,7 @@ <html> <head> <meta charset="utf-8"> - <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>{% block title %}{{ app_config['html_title'] }}{% endblock %}</title> <link rel="stylesheet" type="text/css" href="{{ request.staticdirect('/css/extlib/reset.css') }}"/> @@ -36,44 +36,42 @@ </head> <body> {% block mediagoblin_body %} - <div class="container"> {% block mediagoblin_header %} - <header> - {% block mediagoblin_logo %} - <a class="logo" - href="{{ request.urlgen('index') }}" - ><img src="{{ request.staticdirect('/images/logo.png') }}" - alt="{% trans %}MediaGoblin logo{% endtrans %}" /></a> - {% endblock mediagoblin_logo %} - {% if request.user and request.user.status == 'active' %} - <a class="button_action" - href="{{ request.urlgen('mediagoblin.submit.start') }}"> - {% trans %}Add media{% endtrans %} - </a> - {% endif %} - {% block mediagoblin_header_title %}{% endblock %} - <div class="header_right"> - {% if request.user %} - {# the following link should only appear when verification is needed #} - {% if request.user.status == "needs_email_verification" %} - <a href="{{ request.urlgen('mediagoblin.user_pages.user_home', - user=request.user.username) }}" - class="button_action_highlight"> - {% trans %}Verify your email!{% endtrans %}</a> - {% endif %} - + <header> + {% block mediagoblin_logo %} + <a class="logo" + href="{{ request.urlgen('index') }}" + ><img src="{{ request.staticdirect('/images/logo.png') }}" + alt="{% trans %}MediaGoblin logo{% endtrans %}" /></a> + {% endblock mediagoblin_logo %} + {% if request.user and request.user.status == 'active' %} + <a class="button_action" + href="{{ request.urlgen('mediagoblin.submit.start') }}"> + {% trans %}Add media{% endtrans %} + </a> + {% endif %} + {% block mediagoblin_header_title %}{% endblock %} + <div class="header_right"> + {% if request.user %} + {# the following link should only appear when verification is needed #} + {% if request.user.status == "needs_email_verification" %} <a href="{{ request.urlgen('mediagoblin.user_pages.user_home', - user= request.user.username) }}"> - {{ request.user.username }}</a> - - (<a href="{{ request.urlgen('mediagoblin.auth.logout') }}">{% trans %}log out{% endtrans %}</a>) - {% else %} - <a href="{{ request.urlgen('mediagoblin.auth.login') }}"> - {% trans %}Log in{% endtrans %}</a> + user=request.user.username) }}" + class="button_action_highlight"> + {% trans %}Verify your email!{% endtrans %}</a> {% endif %} - </div> - </header> + <a href="{{ request.urlgen('mediagoblin.user_pages.user_home', + user= request.user.username) }}"> + {{ request.user.username }}</a> + (<a href="{{ request.urlgen('mediagoblin.auth.logout') }}">{% trans %}log out{% endtrans %}</a>) + {% else %} + <a href="{{ request.urlgen('mediagoblin.auth.login') }}"> + {% trans %}Log in{% endtrans %}</a> + {% endif %} + </div> + </header> {% endblock %} + <div class="container"> <div class="mediagoblin_content"> {% include "mediagoblin/utils/messages.html" %} {% block mediagoblin_content %} @@ -82,7 +80,10 @@ {% block mediagoblin_footer %} <footer> {% trans -%} - Powered by <a href="http://mediagoblin.org">MediaGoblin</a>, a <a href="http://gnu.org/">GNU</a> project + Powered by <a href="http://mediagoblin.org">MediaGoblin</a>, a <a href="http://gnu.org/">GNU</a> project. + {%- endtrans %} + {% trans source_link=app_config['source_link'] -%} + Released under the <a href="http://www.fsf.org/licensing/licenses/agpl-3.0.html">AGPL</a>. <a href="{{ source_link }}">Source code</a> available. {%- endtrans %} </footer> {% endblock mediagoblin_footer %} diff --git a/mediagoblin/templates/mediagoblin/edit/edit.html b/mediagoblin/templates/mediagoblin/edit/edit.html index 0c4d5bb1..3f7cbe0e 100644 --- a/mediagoblin/templates/mediagoblin/edit/edit.html +++ b/mediagoblin/templates/mediagoblin/edit/edit.html @@ -33,7 +33,7 @@ </div> {{ wtforms_util.render_divs(form) }} <div class="form_submit_buttons"> - <a href="{{ media.url_for_self(request.urlgen) }}">{% trans %}Cancel{% endtrans %}</a> + <a class="button_action" href="{{ media.url_for_self(request.urlgen) }}">{% trans %}Cancel{% endtrans %}</a> <input type="submit" value="{% trans %}Save changes{% endtrans %}" class="button_form" /> {{ csrf_token }} </div> diff --git a/mediagoblin/templates/mediagoblin/media_displays/video.html b/mediagoblin/templates/mediagoblin/media_displays/video.html index 84631ff5..cf68b61b 100644 --- a/mediagoblin/templates/mediagoblin/media_displays/video.html +++ b/mediagoblin/templates/mediagoblin/media_displays/video.html @@ -18,11 +18,18 @@ {% extends 'mediagoblin/user_pages/media.html' %} +{% block mediagoblin_head %} + {{ super() }} + <script type="text/javascript" + src="{{ request.staticdirect('/js/extlib/video-js/video.js') }}"></script> + <link href="{{ request.staticdirect('/js/extlib/video-js/video-js.css') }}" rel="stylesheet"> +{% endblock %} + {% block mediagoblin_media %} <div class="video-player" style="position: relative;"> <video class="video-js vjs-default-skin" - width="{{ media.media_data.video.width }}" - height="{{ media.media_data.video.height }}" + width="{{ media.media_data.width }}" + height="{{ media.media_data.height }}" controls="controls" preload="metadata" data-setup=""> diff --git a/mediagoblin/templates/mediagoblin/user_pages/media.html b/mediagoblin/templates/mediagoblin/user_pages/media.html index d2503a4e..70a367f9 100644 --- a/mediagoblin/templates/mediagoblin/user_pages/media.html +++ b/mediagoblin/templates/mediagoblin/user_pages/media.html @@ -27,6 +27,8 @@ href="{{ request.staticdirect('/extlib/leaflet/leaflet.ie.css') }}" /><![endif]--> <script type="text/javascript" src="{{ request.staticdirect('/js/comment_show.js') }}"></script> + <script type="text/javascript" + src="{{ request.staticdirect('/js/keyboard_navigation.js') }}"></script> {% if app_config['geolocation_map_visible'] %} <link rel="stylesheet" @@ -40,7 +42,16 @@ {% endblock mediagoblin_head %} {% block mediagoblin_content %} - <div class="media_pane"> + <p class="context"> + {%- trans user_url=request.urlgen( + 'mediagoblin.user_pages.user_home', + user=media.get_uploader.username), + username=media.get_uploader.username -%} + â– Browsing media by <a href="{{user_url}}">{{username}}</a> + {%- endtrans -%} + </p> + {% include "mediagoblin/utils/prev_next.html" %} + <div class="media_pane"> <div class="media_image_container"> {% block mediagoblin_media %} {% set display_media = request.app.public_store.file_url( @@ -65,42 +76,52 @@ <h2 class="media_title"> {{ media.title }} </h2> + {% if request.user and + (media.uploader == request.user._id or + request.user.is_admin) %} + {% set edit_url = request.urlgen('mediagoblin.edit.edit_media', + user= media.get_uploader.username, + media= media._id) %} + <a class="button_action" href="{{ edit_url }}">{% trans %}Edit{% endtrans %}</a> + {% set delete_url = request.urlgen('mediagoblin.user_pages.media_confirm_delete', + user= media.get_uploader.username, + media= media._id) %} + <a class="button_action" href="{{ delete_url }}">{% trans %}Delete{% endtrans %}</a> + {% endif %} {% autoescape False %} <p>{{ media.description_html }}</p> - {% endautoescape %} - <p class="media_specs"> - {% trans date=media.created.strftime("%Y-%m-%d") -%} - Added on {{ date }}. - {%- endtrans %} - {% if request.user and - (media.uploader == request.user._id or - request.user.is_admin) %} - {% set edit_url = request.urlgen('mediagoblin.edit.edit_media', - user= media.get_uploader.username, - media= media._id) %} - <a class="button_action" href="{{ edit_url }}">{% trans %}Edit{% endtrans %}</a> - {% set delete_url = request.urlgen('mediagoblin.user_pages.media_confirm_delete', - user= media.get_uploader.username, - media= media._id) %} - <a class="button_action" href="{{ delete_url }}">{% trans %}Delete{% endtrans %}</a> - {% endif %} - </p> + {% endautoescape %} + {% if media.attachment_files|count %} + <h3>Attachments</h3> + <ul> + {% for attachment in media.attachment_files %} + <li> + <a href="{{ request.app.public_store.file_url(attachment.filepath) }}"> + {{ attachment.name }} + </a> + </li> + {% endfor %} + </ul> + {% endif %} + {% if app_config['allow_attachments'] + and request.user + and (media.uploader == request.user._id + or request.user.is_admin) %} + <p> + <a href="{{ request.urlgen('mediagoblin.edit.attachments', + user=media.get_uploader.username, + media=media._id) }}">Add attachment</a> + </p> + {% endif %} {% if comments %} <h3> - {% if comments.count()==1 %} - {% trans comment_count=comments.count() -%}{{ comment_count }} comment{%- endtrans %} - {% elif comments.count()>1 %} - {% trans comment_count=comments.count() -%}{{ comment_count }} comments{%- endtrans %} - {% else %} - {% trans %}No comments yet.{% endtrans %} - {% endif %} <div class="right_align"> <a {% if not request.user %} href="{{ request.urlgen('mediagoblin.auth.login') }}" {% endif %} class="button_action" id="button_addcomment" title="Add a comment"> - {% trans %}Add one{% endtrans %} + {% trans %}Add a comment{% endtrans %} </a> </div> </h3> @@ -133,7 +154,7 @@ <img src="{{ request.staticdirect('/images/icon_comment.png') }}" /> <a href="{{ request.urlgen('mediagoblin.user_pages.user_home', user = comment_author.username) }}"> - {{ comment_author.username }} + {{ comment_author.username -}} </a> {% trans %}at{% endtrans %} <a href="{{ request.urlgen('mediagoblin.user_pages.media_home.view_comment', @@ -150,35 +171,10 @@ {% endif %} </div> <div class="media_sidebar"> - {% trans user_url=request.urlgen( - 'mediagoblin.user_pages.user_home', - user=media.get_uploader.username), - username=media.get_uploader.username -%} - <p>â– Browsing media by <a href="{{ user_url }}">{{ username }}</a></p> - {%- endtrans %} - {% include "mediagoblin/utils/prev_next.html" %} - {% if media.attachment_files|count %} - <h3>Attachments</h3> - <ul> - {% for attachment in media.attachment_files %} - <li> - <a href="{{ request.app.public_store.file_url(attachment.filepath) }}"> - {{ attachment.name }} - </a> - </li> - {% endfor %} - </ul> - {% endif %} - {% if app_config['allow_attachments'] - and request.user - and (media.uploader == request.user._id - or request.user.is_admin) %} - <p> - <a href="{{ request.urlgen('mediagoblin.edit.attachments', - user=media.get_uploader.username, - media=media._id) }}">Add attachment</a> - </p> - {% endif %} + {% trans date=media.created.strftime("%Y-%m-%d") -%} + <h3>Added on</h3> + <p>{{ date }}</p> + {%- endtrans %} {% if media.tags %} {% include "mediagoblin/utils/tags.html" %} {% endif %} @@ -189,4 +185,5 @@ {% include "mediagoblin/utils/exif.html" %} </div> + <div class="clear"></div> {% endblock %} diff --git a/mediagoblin/templates/mediagoblin/user_pages/media_confirm_delete.html b/mediagoblin/templates/mediagoblin/user_pages/media_confirm_delete.html index e64845e7..a3459206 100644 --- a/mediagoblin/templates/mediagoblin/user_pages/media_confirm_delete.html +++ b/mediagoblin/templates/mediagoblin/user_pages/media_confirm_delete.html @@ -46,8 +46,8 @@ <div class="form_submit_buttons"> {# TODO: This isn't a button really... might do unexpected things :) #} - <a class="cancel_link" href="{{ media.url_for_self(request.urlgen) }}">{% trans %}Cancel{% endtrans %}</a> - <input type="submit" value="{% trans %}Delete Permanently{% endtrans %}" class="button_form" /> + <a class="button_action" href="{{ media.url_for_self(request.urlgen) }}">{% trans %}Cancel{% endtrans %}</a> + <input type="submit" value="{% trans %}Delete permanently{% endtrans %}" class="button_form" /> {{ csrf_token }} </div> </div> diff --git a/mediagoblin/templates/mediagoblin/utils/exif.html b/mediagoblin/templates/mediagoblin/utils/exif.html index 0dd187f2..a89e69c8 100644 --- a/mediagoblin/templates/mediagoblin/utils/exif.html +++ b/mediagoblin/templates/mediagoblin/utils/exif.html @@ -17,12 +17,13 @@ #} {% block exif_content %} - {% if media.media_data.has_key('exif') - and app_config['exif_visible'] - and media.media_data.exif.has_key('useful') %} - <h4>EXIF</h4> + {% if app_config['exif_visible'] + and media.media_data + and media.media_data.exif_all is defined + and media.media_data.exif_all %} + <h3>EXIF</h3> <table> - {% for key, tag in media.media_data.exif.useful.items() %} + {% for key, tag in media.exif_display_iter() %} <tr> <td>{{ key }}</td> <td>{{ tag.printable }}</td> diff --git a/mediagoblin/templates/mediagoblin/utils/geolocation_map.html b/mediagoblin/templates/mediagoblin/utils/geolocation_map.html index c1909ae5..cd57d1f8 100644 --- a/mediagoblin/templates/mediagoblin/utils/geolocation_map.html +++ b/mediagoblin/templates/mediagoblin/utils/geolocation_map.html @@ -17,24 +17,27 @@ #} {% block geolocation_map %} - {% if media.media_data.has_key('gps') - and app_config['geolocation_map_visible'] - and media.media_data.gps %} - <h4>Map</h4> + {% if app_config['geolocation_map_visible'] + and media.media_data.gps_latitude is defined + and media.media_data.gps_latitude + and media.media_data.gps_longitude is defined + and media.media_data.gps_longitude %} + <h3>{% trans %}Location{% endtrans %}</h3> <div> - {% set gps = media.media_data.gps %} + {%- set lon = media.media_data.gps_longitude %} + {%- set lat = media.media_data.gps_latitude %} + {%- set osm_url = "http://openstreetmap.org/?mlat={lat}&mlon={lon}".format(lat=lat, lon=lon) %} <div id="tile-map" style="width: 100%; height: 196px;"> <input type="hidden" id="gps-longitude" - value="{{ gps.longitude }}" /> + value="{{ lon }}" /> <input type="hidden" id="gps-latitude" - value="{{ gps.latitude }}" /> + value="{{ lat }}" /> </div> <p> <small> - View on - <a href="http://openstreetmap.org/?mlat={{ gps.latitude }}&mlon={{ gps.longitude }}"> - OpenStreetMap - </a> + {% trans -%} + View on <a href="{{ osm_url }}">OpenStreetMap</a> + {%- endtrans %} </small> </p> </div> diff --git a/mediagoblin/templates/mediagoblin/utils/license.html b/mediagoblin/templates/mediagoblin/utils/license.html index 2438ed4e..9dad7419 100644 --- a/mediagoblin/templates/mediagoblin/utils/license.html +++ b/mediagoblin/templates/mediagoblin/utils/license.html @@ -17,8 +17,8 @@ #} {% block license_content -%} + <h3>{% trans %}License{% endtrans %}</h3> <p> - {% trans %}License:{% endtrans %} {% if media.license %} <a href="{{ media.license }}">{{ media.get_license_data().abbreviation }}</a> {% else %} diff --git a/mediagoblin/templates/mediagoblin/utils/prev_next.html b/mediagoblin/templates/mediagoblin/utils/prev_next.html index d0cf3f8c..9e262ed9 100644 --- a/mediagoblin/templates/mediagoblin/utils/prev_next.html +++ b/mediagoblin/templates/mediagoblin/utils/prev_next.html @@ -21,26 +21,28 @@ {% set next_entry_url = media.url_to_next(request.urlgen) %} {% if prev_entry_url or next_entry_url %} - {# There are no previous entries for the very first media entry #} - {% if prev_entry_url %} - <a class="navigation_button navigation_left" href="{{ prev_entry_url }}"> - ← {% trans %}newer{% endtrans %} - </a> - {% else %} - {# This is the first entry. display greyed-out 'previous' image #} - <p class="navigation_button navigation_left"> - ← {% trans %}newer{% endtrans %} - </p> - {% endif %} - {# Likewise, this could be the very last media entry #} - {% if next_entry_url %} - <a class="navigation_button" href="{{ next_entry_url }}"> - {% trans %}older{% endtrans %} → - </a> - {% else %} - {# This is the last entry. display greyed-out 'next' image #} - <p class="navigation_button"> - {% trans %}older{% endtrans %} → - </p> - {% endif %} + <div class="navigation"> + {# There are no previous entries for the very first media entry #} + {% if prev_entry_url %} + <a class="navigation_button navigation_left" href="{{ prev_entry_url }}"> + ← {% trans %}newer{% endtrans %} + </a> + {% else %} + {# This is the first entry. display greyed-out 'previous' image #} + <p class="navigation_button navigation_left"> + ← {% trans %}newer{% endtrans %} + </p> + {% endif %} + {# Likewise, this could be the very last media entry #} + {% if next_entry_url %} + <a class="navigation_button navigation_right" href="{{ next_entry_url }}"> + {% trans %}older{% endtrans %} → + </a> + {% else %} + {# This is the last entry. display greyed-out 'next' image #} + <p class="navigation_button navigation_right"> + {% trans %}older{% endtrans %} → + </p> + {% endif %} + </div> {% endif %} diff --git a/mediagoblin/templates/mediagoblin/utils/tags.html b/mediagoblin/templates/mediagoblin/utils/tags.html index 6408102d..0127035c 100644 --- a/mediagoblin/templates/mediagoblin/utils/tags.html +++ b/mediagoblin/templates/mediagoblin/utils/tags.html @@ -17,16 +17,17 @@ #} {% block tags_content -%} - <p>{% trans %}View more media tagged with{% endtrans %} + <h3>{% trans %}Tagged with{% endtrans %}</h3> + <p> {% for tag in media.tags %} {% if loop.last %} {# the 'and' should only appear if there is more than one tag #} {% if media.tags|length > 1 %} - {% trans %}or{% endtrans %} + · {% endif %} <a href="{{ request.urlgen( 'mediagoblin.listings.tags_listing', - tag=tag['slug']) }}">{{ tag['name'] }}</a>. + tag=tag['slug']) }}">{{ tag['name'] }}</a> {% elif loop.revindex == 2 %} <a href="{{ request.urlgen( 'mediagoblin.listings.tags_listing', @@ -34,7 +35,7 @@ {% else %} <a href="{{ request.urlgen( 'mediagoblin.listings.tags_listing', - tag=tag['slug']) }}">{{ tag['name'] }}</a>, + tag=tag['slug']) }}">{{ tag['name'] }}</a> · {% endif %} {% endfor %} </p> diff --git a/mediagoblin/tests/fake_carrot_conf_bad.ini b/mediagoblin/tests/fake_carrot_conf_bad.ini index 0c79b354..9d8cf518 100644 --- a/mediagoblin/tests/fake_carrot_conf_bad.ini +++ b/mediagoblin/tests/fake_carrot_conf_bad.ini @@ -11,4 +11,4 @@ encouragement_phrase = 586956856856 # shouldn't throw error blah_blah = "blah!" # shouldn't throw error either [celery] -eat_celery_with_carrots = pants # yeah that's def an error right there. +EAT_CELERY_WITH_CARROTS = pants # yeah that's def an error right there. diff --git a/mediagoblin/tests/fake_carrot_conf_good.ini b/mediagoblin/tests/fake_carrot_conf_good.ini index fed14d07..1377907b 100644 --- a/mediagoblin/tests/fake_carrot_conf_good.ini +++ b/mediagoblin/tests/fake_carrot_conf_good.ini @@ -10,4 +10,4 @@ encouragement_phrase = "I'd love it if you eat your carrots!" blah_blah = "blah!" [celery] -eat_celery_with_carrots = False +EAT_CELERY_WITH_CARROTS = False diff --git a/mediagoblin/tests/fake_celery_conf.ini b/mediagoblin/tests/fake_celery_conf.ini index 3e52ac3a..67b0cba6 100644 --- a/mediagoblin/tests/fake_celery_conf.ini +++ b/mediagoblin/tests/fake_celery_conf.ini @@ -1,9 +1,9 @@ -['mediagoblin'] +[mediagoblin] # I got nothin' in this file! -['celery'] -some_variable = floop -mail_port = 2000 -celeryd_eta_scheduler_precision = 1.3 -celery_result_persistent = true -celery_imports = foo.bar.baz, this.is.an.import +[celery] +SOME_VARIABLE = floop +MAIL_PORT = 2000 +CELERYD_ETA_SCHEDULER_PRECISION = 1.3 +CELERY_RESULT_PERSISTENT = true +CELERY_IMPORTS = foo.bar.baz, this.is.an.import diff --git a/mediagoblin/tests/fake_celery_conf_mgdb.ini b/mediagoblin/tests/fake_celery_conf_mgdb.ini deleted file mode 100644 index 52671c14..00000000 --- a/mediagoblin/tests/fake_celery_conf_mgdb.ini +++ /dev/null @@ -1,14 +0,0 @@ -['mediagoblin'] -db_host = mongodb.example.org -db_port = 8080 -db_name = captain_lollerskates - -['something'] -or = other - -['celery'] -some_variable = poolf -mail_port = 2020 -celeryd_eta_scheduler_precision = 3.1 -celery_result_persistent = false -celery_imports = baz.bar.foo, import.is.a.this diff --git a/mediagoblin/tests/fake_config_spec.ini b/mediagoblin/tests/fake_config_spec.ini index 9421ce36..43f2e236 100644 --- a/mediagoblin/tests/fake_config_spec.ini +++ b/mediagoblin/tests/fake_config_spec.ini @@ -7,4 +7,4 @@ num_carrots = integer(default=1) encouragement_phrase = string() [celery] -eat_celery_with_carrots = boolean(default=True)
\ No newline at end of file +EAT_CELERY_WITH_CARROTS = boolean(default=True)
\ No newline at end of file diff --git a/mediagoblin/tests/test_celery_setup.py b/mediagoblin/tests/test_celery_setup.py index c9c77821..5530c6f2 100644 --- a/mediagoblin/tests/test_celery_setup.py +++ b/mediagoblin/tests/test_celery_setup.py @@ -22,8 +22,6 @@ from mediagoblin.init.config import read_mediagoblin_config TEST_CELERY_CONF_NOSPECIALDB = pkg_resources.resource_filename( 'mediagoblin.tests', 'fake_celery_conf.ini') -TEST_CELERY_CONF_MGSPECIALDB = pkg_resources.resource_filename( - 'mediagoblin.tests', 'fake_celery_conf_mgdb.ini') def test_setup_celery_from_config(): @@ -50,36 +48,13 @@ def test_setup_celery_from_config(): assert isinstance(fake_celery_module.CELERYD_ETA_SCHEDULER_PRECISION, float) assert fake_celery_module.CELERY_RESULT_PERSISTENT is True assert fake_celery_module.CELERY_IMPORTS == [ - 'foo.bar.baz', 'this.is.an.import', 'mediagoblin.processing'] - assert fake_celery_module.CELERY_MONGODB_BACKEND_SETTINGS == { - 'database': 'mediagoblin'} - assert fake_celery_module.CELERY_RESULT_BACKEND == 'mongodb' - assert fake_celery_module.BROKER_BACKEND == 'mongodb' - - _wipe_testmodule_clean(fake_celery_module) - - global_config, validation_result = read_mediagoblin_config( - TEST_CELERY_CONF_MGSPECIALDB) - app_config = global_config['mediagoblin'] - - celery_setup.setup_celery_from_config( - app_config, global_config, - 'mediagoblin.tests.fake_celery_module', set_environ=False) - - from mediagoblin.tests import fake_celery_module - assert fake_celery_module.SOME_VARIABLE == 'poolf' - assert fake_celery_module.MAIL_PORT == 2020 - assert isinstance(fake_celery_module.MAIL_PORT, int) - assert fake_celery_module.CELERYD_ETA_SCHEDULER_PRECISION == 3.1 - assert isinstance(fake_celery_module.CELERYD_ETA_SCHEDULER_PRECISION, float) - assert fake_celery_module.CELERY_RESULT_PERSISTENT is False - assert fake_celery_module.CELERY_IMPORTS == [ - 'baz.bar.foo', 'import.is.a.this', 'mediagoblin.processing'] - assert fake_celery_module.CELERY_MONGODB_BACKEND_SETTINGS == { - 'database': 'captain_lollerskates', - 'host': 'mongodb.example.org', - 'port': 8080} - assert fake_celery_module.CELERY_RESULT_BACKEND == 'mongodb' - assert fake_celery_module.BROKER_BACKEND == 'mongodb' - assert fake_celery_module.BROKER_HOST == 'mongodb.example.org' - assert fake_celery_module.BROKER_PORT == 8080 + 'foo.bar.baz', 'this.is.an.import', 'mediagoblin.processing.task'] + assert fake_celery_module.CELERY_RESULT_BACKEND == 'database' + assert fake_celery_module.CELERY_RESULT_DBURI == ( + 'sqlite:///' + + pkg_resources.resource_filename('mediagoblin.tests', 'celery.db')) + + assert fake_celery_module.BROKER_TRANSPORT == 'sqlalchemy' + assert fake_celery_module.BROKER_HOST == ( + 'sqlite:///' + + pkg_resources.resource_filename('mediagoblin.tests', 'kombu.db')) diff --git a/mediagoblin/tests/test_config.py b/mediagoblin/tests/test_config.py index c596f6a6..7d8c65c1 100644 --- a/mediagoblin/tests/test_config.py +++ b/mediagoblin/tests/test_config.py @@ -37,7 +37,7 @@ def test_read_mediagoblin_config(): assert this_conf['carrotapp']['carrotcake'] == False assert this_conf['carrotapp']['num_carrots'] == 1 assert not this_conf['carrotapp'].has_key('encouragement_phrase') - assert this_conf['celery']['eat_celery_with_carrots'] == True + assert this_conf['celery']['EAT_CELERY_WITH_CARROTS'] == True # A good file this_conf, validation_results = config.read_mediagoblin_config( @@ -48,7 +48,7 @@ def test_read_mediagoblin_config(): assert this_conf['carrotapp']['encouragement_phrase'] == \ "I'd love it if you eat your carrots!" assert this_conf['carrotapp']['blah_blah'] == "blah!" - assert this_conf['celery']['eat_celery_with_carrots'] == False + assert this_conf['celery']['EAT_CELERY_WITH_CARROTS'] == False # A bad file this_conf, validation_results = config.read_mediagoblin_config( @@ -61,7 +61,7 @@ def test_read_mediagoblin_config(): assert this_conf['carrotapp']['encouragement_phrase'] == \ "586956856856" assert this_conf['carrotapp']['blah_blah'] == "blah!" - assert this_conf['celery']['eat_celery_with_carrots'] == "pants" + assert this_conf['celery']['EAT_CELERY_WITH_CARROTS'] == "pants" def test_generate_validation_report(): @@ -89,7 +89,7 @@ There were validation problems loading this config file: expected_warnings = [ 'carrotapp:carrotcake = the value "slobber" is of the wrong type.', 'carrotapp:num_carrots = the value "GROSS" is of the wrong type.', - 'celery:eat_celery_with_carrots = the value "pants" is of the wrong type.'] + 'celery:EAT_CELERY_WITH_CARROTS = the value "pants" is of the wrong type.'] warnings = report.splitlines()[2:] assert len(warnings) == 3 diff --git a/mediagoblin/tests/test_mgoblin_app.ini b/mediagoblin/tests/test_mgoblin_app.ini index c91ed92b..01bf0972 100644 --- a/mediagoblin/tests/test_mgoblin_app.ini +++ b/mediagoblin/tests/test_mgoblin_app.ini @@ -26,4 +26,4 @@ data_dir = %(here)s/test_user_dev/beaker/cache/data lock_dir = %(here)s/test_user_dev/beaker/cache/lock [celery] -celery_always_eager = true +CELERY_ALWAYS_EAGER = true diff --git a/mediagoblin/tests/test_paste.ini b/mediagoblin/tests/test_paste.ini index bd57994b..d7c18642 100644 --- a/mediagoblin/tests/test_paste.ini +++ b/mediagoblin/tests/test_paste.ini @@ -29,7 +29,7 @@ beaker.session.data_dir = %(here)s/test_user_dev/beaker/sessions/data beaker.session.lock_dir = %(here)s/test_user_dev/beaker/sessions/lock [celery] -celery_always_eager = true +CELERY_ALWAYS_EAGER = true [server:main] use = egg:Paste#http diff --git a/mediagoblin/tests/test_processing.py b/mediagoblin/tests/test_processing.py new file mode 100644 index 00000000..417f91f3 --- /dev/null +++ b/mediagoblin/tests/test_processing.py @@ -0,0 +1,20 @@ +#!/usr/bin/env python + +from nose.tools import assert_equal, assert_true, assert_false + +from mediagoblin import processing + +class TestProcessing(object): + def run_fill(self, input, format, output=None): + builder = processing.FilenameBuilder(input) + result = builder.fill(format) + if output is None: + return result + assert_equal(output, result) + + def test_easy_filename_fill(self): + self.run_fill('/home/user/foo.TXT', '{basename}bar{ext}', 'foobar.txt') + + def test_long_filename_fill(self): + self.run_fill('{0}.png'.format('A' * 300), 'image-{basename}{ext}', + 'image-{0}.png'.format('A' * 245)) diff --git a/mediagoblin/tests/test_sql_migrations.py b/mediagoblin/tests/test_sql_migrations.py new file mode 100644 index 00000000..507a7725 --- /dev/null +++ b/mediagoblin/tests/test_sql_migrations.py @@ -0,0 +1,884 @@ +# GNU MediaGoblin -- federated, autonomous media hosting +# Copyright (C) 2012, 2012 MediaGoblin contributors. See AUTHORS. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +import copy + +from sqlalchemy import ( + Table, Column, MetaData, Index, + Integer, Float, Unicode, UnicodeText, DateTime, Boolean, + ForeignKey, UniqueConstraint, PickleType, VARCHAR) +from sqlalchemy.orm import sessionmaker, relationship +from sqlalchemy.ext.declarative import declarative_base +from sqlalchemy.sql import select, insert +from migrate import changeset + +from mediagoblin.db.sql.base import GMGTableBase +from mediagoblin.db.sql.util import MigrationManager, RegisterMigration + + +# This one will get filled with local migrations +FULL_MIGRATIONS = {} + + +####################################################### +# Migration set 1: Define initial models, no migrations +####################################################### + +Base1 = declarative_base(cls=GMGTableBase) + +class Creature1(Base1): + __tablename__ = "creature" + + id = Column(Integer, primary_key=True) + name = Column(Unicode, unique=True, nullable=False, index=True) + num_legs = Column(Integer, nullable=False) + is_demon = Column(Boolean) + +class Level1(Base1): + __tablename__ = "level" + + id = Column(Unicode, primary_key=True) + name = Column(Unicode) + description = Column(Unicode) + exits = Column(PickleType) + +SET1_MODELS = [Creature1, Level1] + +SET1_MIGRATIONS = {} + +####################################################### +# Migration set 2: A few migrations and new model +####################################################### + +Base2 = declarative_base(cls=GMGTableBase) + +class Creature2(Base2): + __tablename__ = "creature" + + id = Column(Integer, primary_key=True) + name = Column(Unicode, unique=True, nullable=False, index=True) + num_legs = Column(Integer, nullable=False) + magical_powers = relationship("CreaturePower2") + +class CreaturePower2(Base2): + __tablename__ = "creature_power" + + id = Column(Integer, primary_key=True) + creature = Column( + Integer, ForeignKey('creature.id'), nullable=False) + name = Column(Unicode) + description = Column(Unicode) + hitpower = Column(Integer, nullable=False) + +class Level2(Base2): + __tablename__ = "level" + + id = Column(Unicode, primary_key=True) + name = Column(Unicode) + description = Column(Unicode) + +class LevelExit2(Base2): + __tablename__ = "level_exit" + + id = Column(Integer, primary_key=True) + name = Column(Unicode) + from_level = Column( + Unicode, ForeignKey('level.id'), nullable=False) + to_level = Column( + Unicode, ForeignKey('level.id'), nullable=False) + +SET2_MODELS = [Creature2, CreaturePower2, Level2, LevelExit2] + + +@RegisterMigration(1, FULL_MIGRATIONS) +def creature_remove_is_demon(db_conn): + """ + Remove the is_demon field from the creature model. We don't need + it! + """ + # :( Commented out 'cuz of: + # http://code.google.com/p/sqlalchemy-migrate/issues/detail?id=143&thanks=143&ts=1327882242 + + # metadata = MetaData(bind=db_conn.bind) + # creature_table = Table( + # 'creature', metadata, + # autoload=True, autoload_with=db_conn.bind) + # creature_table.drop_column('is_demon') + pass + + +@RegisterMigration(2, FULL_MIGRATIONS) +def creature_powers_new_table(db_conn): + """ + Add a new table for creature powers. Nothing needs to go in it + yet though as there wasn't anything that previously held this + information + """ + metadata = MetaData(bind=db_conn.bind) + + # We have to access the creature table so sqlalchemy can make the + # foreign key relationship + creature_table = Table( + 'creature', metadata, + autoload=True, autoload_with=db_conn.bind) + + creature_powers = Table( + 'creature_power', metadata, + Column('id', Integer, primary_key=True), + Column('creature', + Integer, ForeignKey('creature.id'), nullable=False), + Column('name', Unicode), + Column('description', Unicode), + Column('hitpower', Integer, nullable=False)) + metadata.create_all(db_conn.bind) + + +@RegisterMigration(3, FULL_MIGRATIONS) +def level_exits_new_table(db_conn): + """ + Make a new table for level exits and move the previously pickled + stuff over to here (then drop the old unneeded table) + """ + # First, create the table + # ----------------------- + metadata = MetaData(bind=db_conn.bind) + + # Minimal representation of level table. + # Not auto-introspecting here because of pickle table. I'm not + # sure sqlalchemy can auto-introspect pickle columns. + levels = Table( + 'level', metadata, + Column('id', Unicode, primary_key=True), + Column('name', Unicode), + Column('description', Unicode), + Column('exits', PickleType)) + + level_exits = Table( + 'level_exit', metadata, + Column('id', Integer, primary_key=True), + Column('name', Unicode), + Column('from_level', + Unicode, ForeignKey('level.id'), nullable=False), + Column('to_level', + Unicode, ForeignKey('level.id'), nullable=False)) + metadata.create_all(db_conn.bind) + + # And now, convert all the old exit pickles to new level exits + # ------------------------------------------------------------ + + # query over and insert + result = db_conn.execute( + select([levels], levels.c.exits!=None)) + + for level in result: + + for exit_name, to_level in level['exits'].iteritems(): + # Insert the level exit + db_conn.execute( + level_exits.insert().values( + name=exit_name, + from_level=level.id, + to_level=to_level)) + + # Finally, drop the old level exits pickle table + # ---------------------------------------------- + levels.drop_column('exits') + + +# A hack! At this point we freeze-fame and get just a partial list of +# migrations + +SET2_MIGRATIONS = copy.copy(FULL_MIGRATIONS) + +####################################################### +# Migration set 3: Final migrations +####################################################### + +Base3 = declarative_base(cls=GMGTableBase) + +class Creature3(Base3): + __tablename__ = "creature" + + id = Column(Integer, primary_key=True) + name = Column(Unicode, unique=True, nullable=False, index=True) + num_limbs= Column(Integer, nullable=False) + magical_powers = relationship("CreaturePower3") + +class CreaturePower3(Base3): + __tablename__ = "creature_power" + + id = Column(Integer, primary_key=True) + creature = Column( + Integer, ForeignKey('creature.id'), nullable=False, index=True) + name = Column(Unicode) + description = Column(Unicode) + hitpower = Column(Float, nullable=False) + +class Level3(Base3): + __tablename__ = "level" + + id = Column(Unicode, primary_key=True) + name = Column(Unicode) + description = Column(Unicode) + +class LevelExit3(Base3): + __tablename__ = "level_exit" + + id = Column(Integer, primary_key=True) + name = Column(Unicode) + from_level = Column( + Unicode, ForeignKey('level.id'), nullable=False, index=True) + to_level = Column( + Unicode, ForeignKey('level.id'), nullable=False, index=True) + + +SET3_MODELS = [Creature3, CreaturePower3, Level3, LevelExit3] +SET3_MIGRATIONS = FULL_MIGRATIONS + + +@RegisterMigration(4, FULL_MIGRATIONS) +def creature_num_legs_to_num_limbs(db_conn): + """ + Turns out we're tracking all sorts of limbs, not "legs" + specifically. Humans would be 4 here, for instance. So we + renamed the column. + """ + metadata = MetaData(bind=db_conn.bind) + creature_table = Table( + 'creature', metadata, + autoload=True, autoload_with=db_conn.bind) + creature_table.c.num_legs.alter(name=u"num_limbs") + + +@RegisterMigration(5, FULL_MIGRATIONS) +def level_exit_index_from_and_to_level(db_conn): + """ + Index the from and to levels of the level exit table. + """ + metadata = MetaData(bind=db_conn.bind) + level_exit = Table( + 'level_exit', metadata, + autoload=True, autoload_with=db_conn.bind) + Index('ix_level_exit_from_level', + level_exit.c.from_level).create(db_conn.bind) + Index('ix_level_exit_to_level', + level_exit.c.to_level).create(db_conn.bind) + + +@RegisterMigration(6, FULL_MIGRATIONS) +def creature_power_index_creature(db_conn): + """ + Index our foreign key relationship to the creatures + """ + metadata = MetaData(bind=db_conn.bind) + creature_power = Table( + 'creature_power', metadata, + autoload=True, autoload_with=db_conn.bind) + Index('ix_creature_power_creature', + creature_power.c.creature).create(db_conn.bind) + + +@RegisterMigration(7, FULL_MIGRATIONS) +def creature_power_hitpower_to_float(db_conn): + """ + Convert hitpower column on creature power table from integer to + float. + + Turns out we want super precise values of how much hitpower there + really is. + """ + metadata = MetaData(bind=db_conn.bind) + + # We have to access the creature table so sqlalchemy can make the + # foreign key relationship + creature_table = Table( + 'creature', metadata, + autoload=True, autoload_with=db_conn.bind) + + creature_power = Table( + 'creature_power', metadata, + Column('id', Integer, primary_key=True), + Column('creature', Integer, + ForeignKey('creature.id'), nullable=False, + index=True), + Column('name', Unicode), + Column('description', Unicode), + Column('hitpower', Integer, nullable=False)) + + creature_power.c.hitpower.alter(type=Float) + + +def _insert_migration1_objects(session): + """ + Test objects to insert for the first set of things + """ + # Insert creatures + session.add_all( + [Creature1(name=u'centipede', + num_legs=100, + is_demon=False), + Creature1(name=u'wolf', + num_legs=4, + is_demon=False), + # don't ask me what a wizardsnake is. + Creature1(name=u'wizardsnake', + num_legs=0, + is_demon=True)]) + + # Insert levels + session.add_all( + [Level1(id=u'necroplex', + name=u'The Necroplex', + description=u'A complex full of pure deathzone.', + exits={ + 'deathwell': 'evilstorm', + 'portal': 'central_park'}), + Level1(id=u'evilstorm', + name=u'Evil Storm', + description=u'A storm full of pure evil.', + exits={}), # you can't escape the evilstorm + Level1(id=u'central_park', + name=u'Central Park, NY, NY', + description=u"New York's friendly Central Park.", + exits={ + 'portal': 'necroplex'})]) + + session.commit() + + +def _insert_migration2_objects(session): + """ + Test objects to insert for the second set of things + """ + # Insert creatures + session.add_all( + [Creature2( + name=u'centipede', + num_legs=100), + Creature2( + name=u'wolf', + num_legs=4, + magical_powers = [ + CreaturePower2( + name=u"ice breath", + description=u"A blast of icy breath!", + hitpower=20), + CreaturePower2( + name=u"death stare", + description=u"A frightening stare, for sure!", + hitpower=45)]), + Creature2( + name=u'wizardsnake', + num_legs=0, + magical_powers=[ + CreaturePower2( + name=u'death_rattle', + description=u'A rattle... of DEATH!', + hitpower=1000), + CreaturePower2( + name=u'sneaky_stare', + description=u"The sneakiest stare you've ever seen!", + hitpower=300), + CreaturePower2( + name=u'slithery_smoke', + description=u"A blast of slithery, slithery smoke.", + hitpower=10), + CreaturePower2( + name=u'treacherous_tremors', + description=u"The ground shakes beneath footed animals!", + hitpower=0)])]) + + # Insert levels + session.add_all( + [Level2(id=u'necroplex', + name=u'The Necroplex', + description=u'A complex full of pure deathzone.'), + Level2(id=u'evilstorm', + name=u'Evil Storm', + description=u'A storm full of pure evil.', + exits=[]), # you can't escape the evilstorm + Level2(id=u'central_park', + name=u'Central Park, NY, NY', + description=u"New York's friendly Central Park.")]) + + # necroplex exits + session.add_all( + [LevelExit2(name=u'deathwell', + from_level=u'necroplex', + to_level=u'evilstorm'), + LevelExit2(name=u'portal', + from_level=u'necroplex', + to_level=u'central_park')]) + + # there are no evilstorm exits because there is no exit from the + # evilstorm + + # central park exits + session.add_all( + [LevelExit2(name=u'portal', + from_level=u'central_park', + to_level=u'necroplex')]) + + session.commit() + + +def _insert_migration3_objects(session): + """ + Test objects to insert for the third set of things + """ + # Insert creatures + session.add_all( + [Creature3( + name=u'centipede', + num_limbs=100), + Creature3( + name=u'wolf', + num_limbs=4, + magical_powers = [ + CreaturePower3( + name=u"ice breath", + description=u"A blast of icy breath!", + hitpower=20.0), + CreaturePower3( + name=u"death stare", + description=u"A frightening stare, for sure!", + hitpower=45.0)]), + Creature3( + name=u'wizardsnake', + num_limbs=0, + magical_powers=[ + CreaturePower3( + name=u'death_rattle', + description=u'A rattle... of DEATH!', + hitpower=1000.0), + CreaturePower3( + name=u'sneaky_stare', + description=u"The sneakiest stare you've ever seen!", + hitpower=300.0), + CreaturePower3( + name=u'slithery_smoke', + description=u"A blast of slithery, slithery smoke.", + hitpower=10.0), + CreaturePower3( + name=u'treacherous_tremors', + description=u"The ground shakes beneath footed animals!", + hitpower=0.0)])], + # annnnnd one more to test a floating point hitpower + Creature3( + name=u'deity', + numb_limbs=30, + magical_powers=[ + CreaturePower3( + name=u'smite', + description=u'Smitten by holy wrath!', + hitpower=9999.9)])) + + # Insert levels + session.add_all( + [Level3(id=u'necroplex', + name=u'The Necroplex', + description=u'A complex full of pure deathzone.'), + Level3(id=u'evilstorm', + name=u'Evil Storm', + description=u'A storm full of pure evil.', + exits=[]), # you can't escape the evilstorm + Level3(id=u'central_park', + name=u'Central Park, NY, NY', + description=u"New York's friendly Central Park.")]) + + # necroplex exits + session.add_all( + [LevelExit3(name=u'deathwell', + from_level=u'necroplex', + to_level=u'evilstorm'), + LevelExit3(name=u'portal', + from_level=u'necroplex', + to_level=u'central_park')]) + + # there are no evilstorm exits because there is no exit from the + # evilstorm + + # central park exits + session.add_all( + [LevelExit3(name=u'portal', + from_level=u'central_park', + to_level=u'necroplex')]) + + session.commit() + + +class CollectingPrinter(object): + def __init__(self): + self.collection = [] + + def __call__(self, string): + self.collection.append(string) + + @property + def combined_string(self): + return u''.join(self.collection) + + +def create_test_engine(): + from sqlalchemy import create_engine + engine = create_engine('sqlite:///:memory:', echo=False) + Session = sessionmaker(bind=engine) + return engine, Session + + +def assert_col_type(column, this_class): + assert isinstance(column.type, this_class) + + +def _get_level3_exits(session, level): + return dict( + [(level_exit.name, level_exit.to_level) + for level_exit in + session.query(LevelExit3).filter_by(from_level=level.id)]) + + +def test_set1_to_set3(): + # Create / connect to database + # ---------------------------- + + engine, Session = create_test_engine() + + # Create tables by migrating on empty initial set + # ----------------------------------------------- + + printer = CollectingPrinter() + migration_manager = MigrationManager( + '__main__', SET1_MODELS, SET1_MIGRATIONS, Session(), + printer) + + # Check latest migration and database current migration + assert migration_manager.latest_migration == 0 + assert migration_manager.database_current_migration == None + + result = migration_manager.init_or_migrate() + + # Make sure output was "inited" + assert result == u'inited' + # Check output + assert printer.combined_string == ( + "-> Initializing main mediagoblin tables... done.\n") + # Check version in database + assert migration_manager.latest_migration == 0 + assert migration_manager.database_current_migration == 0 + + # Install the initial set + # ----------------------- + + _insert_migration1_objects(Session()) + + # Try to "re-migrate" with same manager settings... nothing should happen + migration_manager = MigrationManager( + '__main__', SET1_MODELS, SET1_MIGRATIONS, Session(), + printer) + assert migration_manager.init_or_migrate() == None + + # Check version in database + assert migration_manager.latest_migration == 0 + assert migration_manager.database_current_migration == 0 + + # Sanity check a few things in the database... + metadata = MetaData(bind=engine) + + # Check the structure of the creature table + creature_table = Table( + 'creature', metadata, + autoload=True, autoload_with=engine) + assert set(creature_table.c.keys()) == set( + ['id', 'name', 'num_legs', 'is_demon']) + assert_col_type(creature_table.c.id, Integer) + assert_col_type(creature_table.c.name, VARCHAR) + assert creature_table.c.name.nullable is False + #assert creature_table.c.name.index is True + #assert creature_table.c.name.unique is True + assert_col_type(creature_table.c.num_legs, Integer) + assert creature_table.c.num_legs.nullable is False + assert_col_type(creature_table.c.is_demon, Boolean) + + # Check the structure of the level table + level_table = Table( + 'level', metadata, + autoload=True, autoload_with=engine) + assert set(level_table.c.keys()) == set( + ['id', 'name', 'description', 'exits']) + assert_col_type(level_table.c.id, VARCHAR) + assert level_table.c.id.primary_key is True + assert_col_type(level_table.c.name, VARCHAR) + assert_col_type(level_table.c.description, VARCHAR) + # Skipping exits... Not sure if we can detect pickletype, not a + # big deal regardless. + + # Now check to see if stuff seems to be in there. + session = Session() + + creature = session.query(Creature1).filter_by( + name=u'centipede').one() + assert creature.num_legs == 100 + assert creature.is_demon == False + + creature = session.query(Creature1).filter_by( + name=u'wolf').one() + assert creature.num_legs == 4 + assert creature.is_demon == False + + creature = session.query(Creature1).filter_by( + name=u'wizardsnake').one() + assert creature.num_legs == 0 + assert creature.is_demon == True + + level = session.query(Level1).filter_by( + id=u'necroplex').one() + assert level.name == u'The Necroplex' + assert level.description == u'A complex full of pure deathzone.' + assert level.exits == { + 'deathwell': 'evilstorm', + 'portal': 'central_park'} + + level = session.query(Level1).filter_by( + id=u'evilstorm').one() + assert level.name == u'Evil Storm' + assert level.description == u'A storm full of pure evil.' + assert level.exits == {} # You still can't escape the evilstorm! + + level = session.query(Level1).filter_by( + id=u'central_park').one() + assert level.name == u'Central Park, NY, NY' + assert level.description == u"New York's friendly Central Park." + assert level.exits == { + 'portal': 'necroplex'} + + # Create new migration manager, but make sure the db migration + # isn't said to be updated yet + printer = CollectingPrinter() + migration_manager = MigrationManager( + '__main__', SET3_MODELS, SET3_MIGRATIONS, Session(), + printer) + + assert migration_manager.latest_migration == 7 + assert migration_manager.database_current_migration == 0 + + # Migrate + result = migration_manager.init_or_migrate() + + # Make sure result was "migrated" + assert result == u'migrated' + + # TODO: Check output to user + assert printer.combined_string == """\ +-> Updating main mediagoblin tables: + + Running migration 1, "creature_remove_is_demon"... done. + + Running migration 2, "creature_powers_new_table"... done. + + Running migration 3, "level_exits_new_table"... done. + + Running migration 4, "creature_num_legs_to_num_limbs"... done. + + Running migration 5, "level_exit_index_from_and_to_level"... done. + + Running migration 6, "creature_power_index_creature"... done. + + Running migration 7, "creature_power_hitpower_to_float"... done. +""" + + # Make sure version matches expected + migration_manager = MigrationManager( + '__main__', SET3_MODELS, SET3_MIGRATIONS, Session(), + printer) + assert migration_manager.latest_migration == 7 + assert migration_manager.database_current_migration == 7 + + # Check all things in database match expected + + # Check the creature table + metadata = MetaData(bind=engine) + creature_table = Table( + 'creature', metadata, + autoload=True, autoload_with=engine) + # assert set(creature_table.c.keys()) == set( + # ['id', 'name', 'num_limbs']) + assert set(creature_table.c.keys()) == set( + [u'id', 'name', u'num_limbs', u'is_demon']) + assert_col_type(creature_table.c.id, Integer) + assert_col_type(creature_table.c.name, VARCHAR) + assert creature_table.c.name.nullable is False + #assert creature_table.c.name.index is True + #assert creature_table.c.name.unique is True + assert_col_type(creature_table.c.num_limbs, Integer) + assert creature_table.c.num_limbs.nullable is False + + # Check the CreaturePower table + creature_power_table = Table( + 'creature_power', metadata, + autoload=True, autoload_with=engine) + assert set(creature_power_table.c.keys()) == set( + ['id', 'creature', 'name', 'description', 'hitpower']) + assert_col_type(creature_power_table.c.id, Integer) + assert_col_type(creature_power_table.c.creature, Integer) + assert creature_power_table.c.creature.nullable is False + assert_col_type(creature_power_table.c.name, VARCHAR) + assert_col_type(creature_power_table.c.description, VARCHAR) + assert_col_type(creature_power_table.c.hitpower, Float) + assert creature_power_table.c.hitpower.nullable is False + + # Check the structure of the level table + level_table = Table( + 'level', metadata, + autoload=True, autoload_with=engine) + assert set(level_table.c.keys()) == set( + ['id', 'name', 'description']) + assert_col_type(level_table.c.id, VARCHAR) + assert level_table.c.id.primary_key is True + assert_col_type(level_table.c.name, VARCHAR) + assert_col_type(level_table.c.description, VARCHAR) + + # Check the structure of the level_exits table + level_exit_table = Table( + 'level_exit', metadata, + autoload=True, autoload_with=engine) + assert set(level_exit_table.c.keys()) == set( + ['id', 'name', 'from_level', 'to_level']) + assert_col_type(level_exit_table.c.id, Integer) + assert_col_type(level_exit_table.c.name, VARCHAR) + assert_col_type(level_exit_table.c.from_level, VARCHAR) + assert level_exit_table.c.from_level.nullable is False + #assert level_exit_table.c.from_level.index is True + assert_col_type(level_exit_table.c.to_level, VARCHAR) + assert level_exit_table.c.to_level.nullable is False + #assert level_exit_table.c.to_level.index is True + + # Now check to see if stuff seems to be in there. + session = Session() + creature = session.query(Creature3).filter_by( + name=u'centipede').one() + assert creature.num_limbs == 100.0 + assert creature.magical_powers == [] + + creature = session.query(Creature3).filter_by( + name=u'wolf').one() + assert creature.num_limbs == 4.0 + assert creature.magical_powers == [] + + creature = session.query(Creature3).filter_by( + name=u'wizardsnake').one() + assert creature.num_limbs == 0.0 + assert creature.magical_powers == [] + + level = session.query(Level3).filter_by( + id=u'necroplex').one() + assert level.name == u'The Necroplex' + assert level.description == u'A complex full of pure deathzone.' + level_exits = _get_level3_exits(session, level) + assert level_exits == { + u'deathwell': u'evilstorm', + u'portal': u'central_park'} + + level = session.query(Level3).filter_by( + id=u'evilstorm').one() + assert level.name == u'Evil Storm' + assert level.description == u'A storm full of pure evil.' + level_exits = _get_level3_exits(session, level) + assert level_exits == {} # You still can't escape the evilstorm! + + level = session.query(Level3).filter_by( + id=u'central_park').one() + assert level.name == u'Central Park, NY, NY' + assert level.description == u"New York's friendly Central Park." + level_exits = _get_level3_exits(session, level) + assert level_exits == { + 'portal': 'necroplex'} + + +#def test_set2_to_set3(): + # Create / connect to database + # Create tables by migrating on empty initial set + + # Install the initial set + # Check version in database + # Sanity check a few things in the database + + # Migrate + # Make sure version matches expected + # Check all things in database match expected + # pass + + +#def test_set1_to_set2_to_set3(): + # Create / connect to database + # Create tables by migrating on empty initial set + + # Install the initial set + # Check version in database + # Sanity check a few things in the database + + # Migrate + # Make sure version matches expected + # Check all things in database match expected + + # Migrate again + # Make sure version matches expected again + # Check all things in database match expected again + + ##### Set2 + # creature_table = Table( + # 'creature', metadata, + # autoload=True, autoload_with=db_conn.bind) + # assert set(creature_table.c.keys()) == set( + # ['id', 'name', 'num_legs']) + # assert_col_type(creature_table.c.id, Integer) + # assert_col_type(creature_table.c.name, VARCHAR) + # assert creature_table.c.name.nullable is False + # assert creature_table.c.name.index is True + # assert creature_table.c.name.unique is True + # assert_col_type(creature_table.c.num_legs, Integer) + # assert creature_table.c.num_legs.nullable is False + + # # Check the CreaturePower table + # creature_power_table = Table( + # 'creature_power', metadata, + # autoload=True, autoload_with=db_conn.bind) + # assert set(creature_power_table.c.keys()) == set( + # ['id', 'creature', 'name', 'description', 'hitpower']) + # assert_col_type(creature_power_table.c.id, Integer) + # assert_col_type(creature_power_table.c.creature, Integer) + # assert creature_power_table.c.creature.nullable is False + # assert_col_type(creature_power_table.c.name, VARCHAR) + # assert_col_type(creature_power_table.c.description, VARCHAR) + # assert_col_type(creature_power_table.c.hitpower, Integer) + # assert creature_power_table.c.hitpower.nullable is False + + # # Check the structure of the level table + # level_table = Table( + # 'level', metadata, + # autoload=True, autoload_with=db_conn.bind) + # assert set(level_table.c.keys()) == set( + # ['id', 'name', 'description']) + # assert_col_type(level_table.c.id, VARCHAR) + # assert level_table.c.id.primary_key is True + # assert_col_type(level_table.c.name, VARCHAR) + # assert_col_type(level_table.c.description, VARCHAR) + + # # Check the structure of the level_exits table + # level_exit_table = Table( + # 'level_exit', metadata, + # autoload=True, autoload_with=db_conn.bind) + # assert set(level_exit_table.c.keys()) == set( + # ['id', 'name', 'from_level', 'to_level']) + # assert_col_type(level_exit_table.c.id, Integer) + # assert_col_type(level_exit_table.c.name, VARCHAR) + # assert_col_type(level_exit_table.c.from_level, VARCHAR) + # assert level_exit_table.c.from_level.nullable is False + # assert_col_type(level_exit_table.c.to_level, VARCHAR) + + # pass diff --git a/mediagoblin/tests/test_submission.py b/mediagoblin/tests/test_submission.py index 702741b4..8bf7d13c 100644 --- a/mediagoblin/tests/test_submission.py +++ b/mediagoblin/tests/test_submission.py @@ -15,30 +15,35 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. import urlparse -import pkg_resources +import os import re from nose.tools import assert_equal, assert_true, assert_false +from pkg_resources import resource_filename -from mediagoblin.tests.tools import setup_fresh_app, get_test_app, \ +from mediagoblin.tests.tools import get_test_app, \ fixture_add_user from mediagoblin import mg_globals -from mediagoblin.tools import template, common - -GOOD_JPG = pkg_resources.resource_filename( - 'mediagoblin.tests', 'test_submission/good.jpg') -GOOD_PNG = pkg_resources.resource_filename( - 'mediagoblin.tests', 'test_submission/good.png') -EVIL_FILE = pkg_resources.resource_filename( - 'mediagoblin.tests', 'test_submission/evil') -EVIL_JPG = pkg_resources.resource_filename( - 'mediagoblin.tests', 'test_submission/evil.jpg') -EVIL_PNG = pkg_resources.resource_filename( - 'mediagoblin.tests', 'test_submission/evil.png') +from mediagoblin.tools import template + + +def resource(filename): + return resource_filename('mediagoblin.tests', 'test_submission/' + filename) + + +GOOD_JPG = resource('good.jpg') +GOOD_PNG = resource('good.png') +EVIL_FILE = resource('evil') +EVIL_JPG = resource('evil.jpg') +EVIL_PNG = resource('evil.png') +BIG_BLUE = resource('bigblue.png') GOOD_TAG_STRING = 'yin,yang' BAD_TAG_STRING = 'rage,' + 'f' * 26 + 'u' * 26 +FORM_CONTEXT = ['mediagoblin/submit/start.html', 'submit_form'] +REQUEST_CONTEXT = ['mediagoblin/user_pages/user.html', 'request'] + class TestSubmission: def setUp(self): @@ -61,163 +66,130 @@ class TestSubmission: def logout(self): self.test_app.get('/auth/logout/') + def do_post(self, data, *context_keys, **kwargs): + url = kwargs.pop('url', '/submit/') + do_follow = kwargs.pop('do_follow', False) + template.clear_test_template_context() + response = self.test_app.post(url, data, **kwargs) + if do_follow: + response.follow() + context_data = template.TEMPLATE_TEST_CONTEXT + for key in context_keys: + context_data = context_data[key] + return response, context_data + + def upload_data(self, filename): + return {'upload_files': [('file', filename)]} + + def check_comments(self, request, media, count): + comments = request.db.MediaComment.find({'media_entry': media._id}) + assert_equal(count, len(list(comments))) + def test_missing_fields(self): # Test blank form # --------------- - template.clear_test_template_context() - response = self.test_app.post( - '/submit/', {}) - context = template.TEMPLATE_TEST_CONTEXT['mediagoblin/submit/start.html'] - form = context['submit_form'] - assert form.file.errors == [u'You must provide a file.'] + response, form = self.do_post({}, *FORM_CONTEXT) + assert_equal(form.file.errors, [u'You must provide a file.']) # Test blank file # --------------- - template.clear_test_template_context() - response = self.test_app.post( - '/submit/', { - 'title': 'test title'}) - context = template.TEMPLATE_TEST_CONTEXT['mediagoblin/submit/start.html'] - form = context['submit_form'] - assert form.file.errors == [u'You must provide a file.'] - - - def test_normal_uploads(self): - # Test JPG - # -------- - template.clear_test_template_context() - response = self.test_app.post( - '/submit/', { - 'title': 'Normal upload 1' - }, upload_files=[( - 'file', GOOD_JPG)]) + response, form = self.do_post({'title': 'test title'}, *FORM_CONTEXT) + assert_equal(form.file.errors, [u'You must provide a file.']) - # User should be redirected - response.follow() - assert_equal( - urlparse.urlsplit(response.location)[2], - '/u/chris/') - assert template.TEMPLATE_TEST_CONTEXT.has_key( - 'mediagoblin/user_pages/user.html') + def check_url(self, response, path): + assert_equal(urlparse.urlsplit(response.location)[2], path) + def check_normal_upload(self, title, filename): + response, context = self.do_post({'title': title}, do_follow=True, + **self.upload_data(filename)) + self.check_url(response, '/u/{0}/'.format(self.test_user.username)) + assert_true('mediagoblin/user_pages/user.html' in context) # Make sure the media view is at least reachable, logged in... - self.test_app.get('/u/chris/m/normal-upload-1/') + url = '/u/{0}/m/{1}/'.format(self.test_user.username, + title.lower().replace(' ', '-')) + self.test_app.get(url) # ... and logged out too. self.logout() - self.test_app.get('/u/chris/m/normal-upload-1/') - # Log back in for the remaining tests. - self.login() + self.test_app.get(url) - # Test PNG - # -------- - template.clear_test_template_context() - response = self.test_app.post( - '/submit/', { - 'title': 'Normal upload 2' - }, upload_files=[( - 'file', GOOD_PNG)]) + def test_normal_jpg(self): + self.check_normal_upload('Normal upload 1', GOOD_JPG) - response.follow() - assert_equal( - urlparse.urlsplit(response.location)[2], - '/u/chris/') - assert template.TEMPLATE_TEST_CONTEXT.has_key( - 'mediagoblin/user_pages/user.html') + def test_normal_png(self): + self.check_normal_upload('Normal upload 2', GOOD_PNG) + + def check_media(self, request, find_data, count=None): + media = request.db.MediaEntry.find(find_data) + if count is not None: + assert_equal(media.count(), count) + if count == 0: + return + return media[0] def test_tags(self): # Good tag string # -------- - template.clear_test_template_context() - response = self.test_app.post( - '/submit/', { - 'title': 'Balanced Goblin', - 'tags': GOOD_TAG_STRING - }, upload_files=[( - 'file', GOOD_JPG)]) - - # New media entry with correct tags should be created - response.follow() - context = template.TEMPLATE_TEST_CONTEXT['mediagoblin/user_pages/user.html'] - request = context['request'] - media = request.db.MediaEntry.find({'title': 'Balanced Goblin'})[0] - assert_equal(media['tags'], + response, request = self.do_post({'title': 'Balanced Goblin', + 'tags': GOOD_TAG_STRING}, + *REQUEST_CONTEXT, do_follow=True, + **self.upload_data(GOOD_JPG)) + media = self.check_media(request, {'title': 'Balanced Goblin'}, 1) + assert_equal(media.tags, [{'name': u'yin', 'slug': u'yin'}, - {'name': u'yang', 'slug': u'yang'}]) + {'name': u'yang', 'slug': u'yang'}]) # Test tags that are too long # --------------- - template.clear_test_template_context() - response = self.test_app.post( - '/submit/', { - 'title': 'Balanced Goblin', - 'tags': BAD_TAG_STRING - }, upload_files=[( - 'file', GOOD_JPG)]) - - # Too long error should be raised - context = template.TEMPLATE_TEST_CONTEXT['mediagoblin/submit/start.html'] - form = context['submit_form'] - assert form.tags.errors == [ - u'Tags must be shorter than 50 characters. Tags that are too long'\ - ': ffffffffffffffffffffffffffuuuuuuuuuuuuuuuuuuuuuuuuuu'] + response, form = self.do_post({'title': 'Balanced Goblin', + 'tags': BAD_TAG_STRING}, + *FORM_CONTEXT, + **self.upload_data(GOOD_JPG)) + assert_equal(form.tags.errors, [ + u'Tags must be shorter than 50 characters. ' \ + 'Tags that are too long: ' \ + 'ffffffffffffffffffffffffffuuuuuuuuuuuuuuuuuuuuuuuuuu']) def test_delete(self): - template.clear_test_template_context() - response = self.test_app.post( - '/submit/', { - 'title': 'Balanced Goblin', - }, upload_files=[( - 'file', GOOD_JPG)]) - - # Post image - response.follow() - - request = template.TEMPLATE_TEST_CONTEXT[ - 'mediagoblin/user_pages/user.html']['request'] - - media = request.db.MediaEntry.find({'title': 'Balanced Goblin'})[0] - - # Does media entry exist? - assert_true(media) + response, request = self.do_post({'title': 'Balanced Goblin'}, + *REQUEST_CONTEXT, do_follow=True, + **self.upload_data(GOOD_JPG)) + media = self.check_media(request, {'title': 'Balanced Goblin'}, 1) + + # Add a comment, so we can test for its deletion later. + self.check_comments(request, media, 0) + comment_url = request.urlgen( + 'mediagoblin.user_pages.media_post_comment', + user=self.test_user.username, media=media._id) + response = self.do_post({'comment_content': 'i love this test'}, + url=comment_url, do_follow=True)[0] + self.check_comments(request, media, 1) # Do not confirm deletion # --------------------------------------------------- - response = self.test_app.post( - request.urlgen('mediagoblin.user_pages.media_confirm_delete', - # No work: user=media.uploader().username, - user=self.test_user.username, - media=media._id), - # no value means no confirm - {}) - - response.follow() - - request = template.TEMPLATE_TEST_CONTEXT[ - 'mediagoblin/user_pages/user.html']['request'] - - media = request.db.MediaEntry.find({'title': 'Balanced Goblin'})[0] - - # Does media entry still exist? - assert_true(media) + delete_url = request.urlgen( + 'mediagoblin.user_pages.media_confirm_delete', + user=self.test_user.username, media=media._id) + # Empty data means don't confirm + response = self.do_post({}, do_follow=True, url=delete_url)[0] + media = self.check_media(request, {'title': 'Balanced Goblin'}, 1) # Confirm deletion # --------------------------------------------------- - response = self.test_app.post( - request.urlgen('mediagoblin.user_pages.media_confirm_delete', - # No work: user=media.uploader().username, - user=self.test_user.username, - media=media._id), - {'confirm': 'y'}) - - response.follow() - - request = template.TEMPLATE_TEST_CONTEXT[ - 'mediagoblin/user_pages/user.html']['request'] + response, request = self.do_post({'confirm': 'y'}, *REQUEST_CONTEXT, + do_follow=True, url=delete_url) + self.check_media(request, {'_id': media._id}, 0) + self.check_comments(request, media, 0) - # Does media entry still exist? - assert_false( - request.db.MediaEntry.find( - {'_id': media._id}).count()) + def test_evil_file(self): + # Test non-suppoerted file with non-supported extension + # ----------------------------------------------------- + response, form = self.do_post({'title': 'Malicious Upload 1'}, + *FORM_CONTEXT, + **self.upload_data(EVIL_FILE)) + assert_equal(len(form.file.errors), 1) + assert_true(re.match( + r'^Could not extract any file extension from ".*?"$', + str(form.file.errors[0]))) def test_sniffing(self): ''' @@ -241,62 +213,43 @@ class TestSubmission: assert media.media_type == 'mediagoblin.media_types.image' - def test_malicious_uploads(self): - # Test non-suppoerted file with non-supported extension - # ----------------------------------------------------- - template.clear_test_template_context() - response = self.test_app.post( - '/submit/', { - 'title': 'Malicious Upload 1' - }, upload_files=[( - 'file', EVIL_FILE)]) - - context = template.TEMPLATE_TEST_CONTEXT['mediagoblin/submit/start.html'] - form = context['submit_form'] - assert 'Sorry, I don\'t support that file type :(' == \ - str(form.file.errors[0]) - assert len(form.file.errors) == 1 - + def check_false_image(self, title, filename): # NOTE: The following 2 tests will ultimately fail, but they # *will* pass the initial form submission step. Instead, # they'll be caught as failures during the processing step. + response, context = self.do_post({'title': title}, do_follow=True, + **self.upload_data(filename)) + self.check_url(response, '/u/{0}/'.format(self.test_user.username)) + entry = mg_globals.database.MediaEntry.find_one({'title': title}) + assert_equal(entry.state, 'failed') + assert_equal(entry.fail_error, u'mediagoblin.processing:BadMediaFail') + def test_evil_jpg(self): # Test non-supported file with .jpg extension # ------------------------------------------- - template.clear_test_template_context() - response = self.test_app.post( - '/submit/', { - 'title': 'Malicious Upload 2' - }, upload_files=[( - 'file', EVIL_JPG)]) - response.follow() - assert_equal( - urlparse.urlsplit(response.location)[2], - '/u/chris/') - - entry = mg_globals.database.MediaEntry.find_one( - {'title': 'Malicious Upload 2'}) - assert_equal(entry.state, 'failed') - assert_equal( - entry['fail_error'], - u'mediagoblin.processing:BadMediaFail') + self.check_false_image('Malicious Upload 2', EVIL_JPG) + def test_evil_png(self): # Test non-supported file with .png extension # ------------------------------------------- - template.clear_test_template_context() - response = self.test_app.post( - '/submit/', { - 'title': 'Malicious Upload 3' - }, upload_files=[( - 'file', EVIL_PNG)]) - response.follow() - assert_equal( - urlparse.urlsplit(response.location)[2], - '/u/chris/') - - entry = mg_globals.database.MediaEntry.find_one( - {'title': 'Malicious Upload 3'}) - assert_equal(entry.state, 'failed') - assert_equal( - entry['fail_error'], - u'mediagoblin.processing:BadMediaFail') + self.check_false_image('Malicious Upload 3', EVIL_PNG) + + def test_processing(self): + data = {'title': 'Big Blue'} + response, request = self.do_post(data, *REQUEST_CONTEXT, do_follow=True, + **self.upload_data(BIG_BLUE)) + media = self.check_media(request, data, 1) + last_size = 1024 ** 3 # Needs to be larger than bigblue.png + for key, basename in (('original', 'bigblue.png'), + ('medium', 'bigblue.medium.png'), + ('thumb', 'bigblue.thumbnail.png')): + # Does the processed image have a good filename? + filename = resource_filename( + 'mediagoblin.tests', + os.path.join('test_user_dev/media/public', + *media['media_files'].get(key, []))) + assert_true(filename.endswith('_' + basename)) + # Is it smaller than the last processed image we looked at? + size = os.stat(filename).st_size + assert_true(last_size > size) + last_size = size diff --git a/mediagoblin/tests/test_submission/bigblue.png b/mediagoblin/tests/test_submission/bigblue.png Binary files differnew file mode 100644 index 00000000..2b2c2a44 --- /dev/null +++ b/mediagoblin/tests/test_submission/bigblue.png diff --git a/mediagoblin/tools/exif.py b/mediagoblin/tools/exif.py index de6dd128..448a342e 100644 --- a/mediagoblin/tools/exif.py +++ b/mediagoblin/tools/exif.py @@ -32,6 +32,13 @@ USEFUL_TAGS = [ 'EXIF UserComment', ] +def exif_image_needs_rotation(exif_tags): + """ + Returns True if EXIF orientation requires rotation + """ + return 'Image Orientation' in exif_tags \ + and exif_tags['Image Orientation'].values[0] != 1 + def exif_fix_image_orientation(im, exif_tags): """ Translate any EXIF orientation to raw orientation diff --git a/mediagoblin/tools/files.py b/mediagoblin/tools/files.py index b2f316b2..25c1a6e6 100644 --- a/mediagoblin/tools/files.py +++ b/mediagoblin/tools/files.py @@ -27,6 +27,6 @@ def delete_media_files(media): mg_globals.public_store.delete_file( listpath) - for attachment in media['attachment_files']: + for attachment in media.attachment_files: mg_globals.public_store.delete_file( attachment['filepath']) diff --git a/mediagoblin/tools/request.py b/mediagoblin/tools/request.py index a45f716a..ae372c92 100644 --- a/mediagoblin/tools/request.py +++ b/mediagoblin/tools/request.py @@ -14,8 +14,12 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. +import logging from mediagoblin.db.util import ObjectId, InvalidId +_log = logging.getLogger(__name__) + + def setup_user_in_request(request): """ Examine a request and tack on a request.user parameter if that's @@ -30,12 +34,12 @@ def setup_user_in_request(request): except InvalidId: user = None else: - user = request.db.User.one({'_id': oid}) + user = request.db.User.find_one({'_id': oid}) if not user: # Something's wrong... this user doesn't exist? Invalidate # this session. - print "Killing session for %r" % request.session['user_id'] + _log.warn("Killing session for user id %r", request.session['user_id']) request.session.invalidate() request.user = user diff --git a/mediagoblin/user_pages/forms.py b/mediagoblin/user_pages/forms.py index acfbadab..f17e6c00 100644 --- a/mediagoblin/user_pages/forms.py +++ b/mediagoblin/user_pages/forms.py @@ -21,7 +21,7 @@ from mediagoblin.tools.translate import fake_ugettext_passthrough as _ class MediaCommentForm(wtforms.Form): comment_content = wtforms.TextAreaField( - _(''), + '', [wtforms.validators.Required()]) diff --git a/mediagoblin/user_pages/views.py b/mediagoblin/user_pages/views.py index 82791278..e69a6ffe 100644 --- a/mediagoblin/user_pages/views.py +++ b/mediagoblin/user_pages/views.py @@ -18,7 +18,6 @@ from webob import exc from mediagoblin import messages, mg_globals from mediagoblin.db.util import DESCENDING, ObjectId -from mediagoblin.tools.text import cleaned_markdown_conversion from mediagoblin.tools.response import render_to_response, render_404, redirect from mediagoblin.tools.translate import pass_to_ugettext as _ from mediagoblin.tools.pagination import Pagination @@ -143,12 +142,11 @@ def media_post_comment(request, media): assert request.method == 'POST' comment = request.db.MediaComment() - comment['media_entry'] = media._id - comment['author'] = request.user._id - comment['content'] = unicode(request.POST['comment_content']) - comment['content_html'] = cleaned_markdown_conversion(comment['content']) + comment.media_entry = media.id + comment.author = request.user.id + comment.content = unicode(request.POST['comment_content']) - if not comment['content'].strip(): + if not comment.content.strip(): messages.add_message( request, messages.ERROR, @@ -175,6 +173,10 @@ def media_confirm_delete(request, media): if form.confirm.data is True: username = media.get_uploader.username + # Delete all the associated comments + for comment in media.get_comments(): + comment.delete() + # Delete all files on the public storage delete_media_files(media) @@ -250,7 +252,7 @@ def atom_feed(request): for entry in cursor: feed.add(entry.get('title'), - entry.get('description_html'), + entry.description_html, id=entry.url_for_self(request.urlgen,qualified=True), content_type='html', author={ @@ -303,7 +305,7 @@ def processing_panel(request): # Get media entries which are in-processing processing_entries = request.db.MediaEntry.find( {'uploader': user._id, - 'state': 'processing'}).sort('created', DESCENDING) + 'state': 'unprocessed'}).sort('created', DESCENDING) # Get media entries which have failed to process failed_entries = request.db.MediaEntry.find( @@ -62,6 +62,9 @@ setup( 'webtest', 'ConfigObj', 'Markdown', + 'sqlalchemy', + 'sqlalchemy-migrate', + 'kombu-sqlalchemy', ## For now we're expecting that users will install this from ## their package managers. # 'lxml', |