From a5b5987a5128e4e04c3ca6ac80771d06cd4a80cc Mon Sep 17 00:00:00 2001 From: Britney Fransen Date: Fri, 21 Oct 2011 16:53:42 -0500 Subject: mythplugins: mytharchive: mythburn.py patch for aspect ratio and video storage groups support http://code.mythtv.org/trac/ticket/10042 --- abs/core/mythtv/stable-0.24/mythplugins/PKGBUILD | 9 +- .../stable-0.24/mythplugins/mythburn.py.patch | 303 +++++++++++++++++++++ .../stable-0.24/mythplugins/qt-4.7-buildfixes.diff | 26 -- 3 files changed, 309 insertions(+), 29 deletions(-) create mode 100644 abs/core/mythtv/stable-0.24/mythplugins/mythburn.py.patch delete mode 100644 abs/core/mythtv/stable-0.24/mythplugins/qt-4.7-buildfixes.diff diff --git a/abs/core/mythtv/stable-0.24/mythplugins/PKGBUILD b/abs/core/mythtv/stable-0.24/mythplugins/PKGBUILD index c919256..83168af 100644 --- a/abs/core/mythtv/stable-0.24/mythplugins/PKGBUILD +++ b/abs/core/mythtv/stable-0.24/mythplugins/PKGBUILD @@ -13,7 +13,7 @@ pkgname=('mytharchive' 'mythweather' 'mythzoneminder') pkgver=0.24 -pkgrel=17 +pkgrel=18 arch=('i686' 'x86_64') url="http://www.mythtv.org" license=('GPL') @@ -23,7 +23,8 @@ makedepends=("mythtv>=${pkgver}" 'mplayer' 'fftw' 'dvdauthor' 'libexif' 'libvisual' 'perl-xml-xpath' 'perl-image-size' 'perl-class-inspector' 'perl-datetime-format-iso8601' 'flac' 'libvorbis' 'python2' 'python-pycurl' 'python-oauth') -source=(ftp://ftp.knoppmyth.net/R6/sources/mythtv-0.24-fixes.tar.bz2) +source=(ftp://ftp.knoppmyth.net/R6/sources/mythtv-0.24-fixes.tar.bz2 + mythburn.py.patch) _gitroot="https://github.com/MythTV/mythtv.git" _gitname="mythtv" @@ -46,6 +47,7 @@ package_mytharchive() { depends=("mythtv>=${pkgver}" 'libxmu' 'pil' 'dvdauthor' 'm2vrequantiser') # replaces=('mytharchive') cd "${srcdir}/${_gitname}/${pkgbase}/mytharchive" + patch mythburn/scripts/mythburn.py < $srcdir/mythburn.py.patch || return 1 make INSTALL_ROOT="${pkgdir}" install || return 1 } @@ -123,4 +125,5 @@ package_mythzoneminder() { cd "${srcdir}/${_gitname}/${pkgbase}/mythzoneminder" make INSTALL_ROOT="${pkgdir}" install || return 1 } -md5sums=('91488a6b1b05d14c5c5c601908b699e5') +md5sums=('91488a6b1b05d14c5c5c601908b699e5' + 'adce485c4765e8d49b91b3ce9c9963ac') diff --git a/abs/core/mythtv/stable-0.24/mythplugins/mythburn.py.patch b/abs/core/mythtv/stable-0.24/mythplugins/mythburn.py.patch new file mode 100644 index 0000000..770119a --- /dev/null +++ b/abs/core/mythtv/stable-0.24/mythplugins/mythburn.py.patch @@ -0,0 +1,303 @@ +--- mythburn.py.orig 2011-09-16 13:41:43.000000000 -0500 ++++ mythburn.py 2011-09-17 10:51:36.000000000 -0500 +@@ -38,7 +38,7 @@ + #****************************************************************************** + + # version of script - change after each update +-VERSION="0.1.20101206-1" ++VERSION="0.1.20110821-1" + + # keep all temporary files for debugging purposes + # set this to True before a first run through when testing +@@ -75,6 +75,7 @@ + from fcntl import ioctl + import CDROM + from shutil import copy ++from subprocess import Popen, PIPE + + # media types (should match the enum in mytharchivewizard.h) + DVD_SL = 0 +@@ -606,7 +607,7 @@ + # of a video file from its stream info file + + def getVideoParams(folder): +- """Returns the video resolution, fps and aspect ratio for the video file from the streamindo.xml file""" ++ """Returns the video resolution, fps and aspect ratio for the video file from the streaminfo.xml file""" + + #open the XML containing information about this file + infoDOM = xml.dom.minidom.parse(os.path.join(folder, 'streaminfo.xml')) +@@ -642,14 +643,14 @@ + # Gets the aspect ratio of a video file from its stream info file + + def getAspectRatioOfVideo(index): +- """Returns the aspect ratio of the video file (1.333, 1.778, etc)""" ++ """Returns the aspect ratio of the original video file (1.333, 1.778, etc)""" + + #open the XML containing information about this file +- infoDOM = xml.dom.minidom.parse(os.path.join(getItemTempPath(index), 'streaminfo.xml')) ++ infoDOM = xml.dom.minidom.parse(os.path.join(getItemTempPath(index), 'streaminfo_orig.xml')) + + #error out if its the wrong XML + if infoDOM.documentElement.tagName != "file": +- fatalError("Stream info file doesn't look right (%s)" % os.path.join(getItemTempPath(index), 'streaminfo.xml')) ++ fatalError("Stream info file doesn't look right (%s)" % os.path.join(getItemTempPath(index), 'streaminfo_orig.xml')) + video = infoDOM.getElementsByTagName("file")[0].getElementsByTagName("streams")[0].getElementsByTagName("video")[0] + if video.attributes["aspectratio"].value != 'N/A': + aspect_ratio = float(video.attributes["aspectratio"].value) +@@ -1762,6 +1763,37 @@ + + + ############################################################# ++# Finds the path of a video file from the local video path ++# or Storage Group ++ ++def getVideoPath(filename): ++ # connect ++ db = getDatabaseConnection() ++ # create a cursor ++ cursor = db.cursor() ++ # execute SQL statement ++ cursor.execute("""SELECT dirname ++ FROM storagegroup ++ WHERE groupname='Videos'""") ++ # get the resultset as a tuple ++ result = cursor.fetchall() ++ # make result a list and add local video path if exists ++ result = [videopath] + list(result) ++ ++ # iterate through result set ++ for sg in result: ++ if doesFileExist(os.path.join("".join(sg), filename)) == True: ++ filepath = "".join(sg) ++ write("Video Path: %s" % filepath) ++ return (filepath) ++ break ++ ++ db.close() ++ del db ++ del cursor ++ ++ ++############################################################# + # Pre-process a single video/recording file + + def preProcessFile(file, folder, count): +@@ -1775,11 +1807,11 @@ + #3. Extract a single frame from the video to use as a thumbnail and resolution check + mediafile="" + +- if file.attributes["type"].value == "recording": ++ if file.attributes["type"].value=="recording": + mediafile = file.attributes["filename"].value +- elif file.attributes["type"].value == "video": +- mediafile = os.path.join(videopath, file.attributes["filename"].value) +- elif file.attributes["type"].value == "file": ++ elif file.attributes["type"].value=="video": ++ mediafile = os.path.join(getVideoPath(file.attributes["filename"].value), file.attributes["filename"].value) ++ elif file.attributes["type"].value=="file": + mediafile = file.attributes["filename"].value + else: + fatalError("Unknown type of video file it must be 'recording', 'video' or 'file'.") +@@ -1935,9 +1967,39 @@ + + if result <> 0: + fatalError("Failed while running mytharchivehelper to get stream information from %s" % filename) ++ ++ #open the XML containing information about this file ++ infoDOM = xml.dom.minidom.parse(xmlFilename) ++ ++ #error out if its the wrong XML ++ if infoDOM.documentElement.tagName != "file": ++ fatalError("This info file doesn't look right (%s)." % xmlFilename) ++ ++ file = infoDOM.getElementsByTagName("file")[0] ++ video = infoDOM.getElementsByTagName("file")[0].getElementsByTagName("streams")[0].getElementsByTagName("video")[0] ++ ++ #use ffmpeg to get display aspect ratio (DAR) of video ++ cmd = path_ffmpeg[0] + " -i " + quoteFilename(file.attributes["filename"].value) + " 2>&1" ++ aspect_ratio = Popen(cmd, shell=True, stdout=PIPE).stdout.read() ++ if "DAR" in aspect_ratio: ++ #clean DAR string ++ aspect_ratio = aspect_ratio.split("DAR ")[-1].split(",")[0] ++ aspect_ratio = ''.join([c for c in aspect_ratio if c in '1234567890:']).split(":") ++ else: ++ #calculate aspect from video size ++ aspect_ratio = getVideoSize(xmlFilename) ++ ++ #convert to decimal ++ aspect_ratio = float(aspect_ratio[0]) / float(aspect_ratio[1]) ++ ++ write("Video %s aspect ratio is: %s" % (filename, aspect_ratio)) ++ ++ #set aspect ratio ++ video.setAttribute("aspectratio",str(aspect_ratio)) ++ ++ WriteXMLToFile (infoDOM,xmlFilename) + + # print out the streaminfo.xml file to the log +- infoDOM = xml.dom.minidom.parse(xmlFilename) + write("streaminfo.xml :-\n" + infoDOM.toprettyxml(" ", ""), False) + + ############################################################# +@@ -2322,7 +2384,7 @@ + ############################################################# + # Re-encodes a file to mpeg2 + +-def encodeVideoToMPEG2(source, destvideofile, video, audio1, audio2, aspectratio, profile): ++def encodeVideoToMPEG2(source, destvideofile, video, folder, audio1, audio2, aspectratio, profile): + """Encodes an unknown video source file eg. AVI to MPEG2 video and AC3 audio, use ffmpeg""" + + profileNode = findEncodingProfile(profile) +@@ -2347,6 +2409,18 @@ + value = quoteFilename(destvideofile) + if value == "%aspect": + value = aspectratio ++ if value == "720x480" or value == "720x576": ++ #add padding to correct for aspects > than 1.9:1 ++ videores, fps, videoAR = getVideoParams(folder) ++ if float(videoAR) >= 1.9: ++ if videomode == "ntsc": ++ videoheight = 480 ++ else: ++ videoheight = 576 ++ ++ croppixels = videoheight - int(videores.split("x")[1]) ++ write("CropPixels Total: %s" % croppixels) ++ value = "720x%d -vf pad=720:%d:0:%d:black" % (videoheight - croppixels, videoheight, croppixels / 2) + + # only re-encode the audio if it is not already in AC3 format + if audio1[AUDIO_CODEC] == "AC3": +@@ -2383,12 +2457,12 @@ + command += " -newaudio" + + #make sure we get the correct stream(s) that we want +- command += " -map 0:%d -map 0:%d " % (video[VIDEO_INDEX], audio1[AUDIO_INDEX]) ++ command += " -map 0:%d -map 0:%d" % (video[VIDEO_INDEX], audio1[AUDIO_INDEX]) + if audio2[AUDIO_ID] != -1: + command += "-map 0:%d" % (audio2[AUDIO_INDEX]) + + if passes == 1: +- write(command) ++ write("Running ffmpeg: %s" % command) + result = runCommand(command) + if result!=0: + fatalError("Failed while running ffmpeg to re-encode video.\n" +@@ -2399,7 +2473,7 @@ + + pass1 = string.replace(command, "%passno","1") + pass1 = string.replace(pass1, "%passlogfile", passLog) +- write("Pass 1 - " + pass1) ++ write("Running ffmpeg Pass 1: %s" % pass1) + result = runCommand(pass1) + + if result!=0: +@@ -2411,7 +2485,7 @@ + + pass2 = string.replace(command, "%passno","2") + pass2 = string.replace(pass2, "%passlogfile", passLog) +- write("Pass 2 - " + pass2) ++ write("Running ffmpeg Pass 2: %s" % pass2) + result = runCommand(pass2) + + if result!=0: +@@ -2443,10 +2517,6 @@ + outaudiosamplerate = 48000 + outaudiocodec = "ac3" + deinterlace = 0 +- croptop = 0 +- cropright = 0 +- cropbottom = 0 +- cropleft = 0 + qmin = 5 + qmax = 31 + qdiff = 31 +@@ -2470,14 +2540,6 @@ + outvideores = value + if name == "-deinterlace": + deinterlace = 1 +- if name == "-croptop": +- croptop = value +- if name == "-cropright": +- cropright = value +- if name == "-cropbottom": +- cropbottom = value +- if name == "-cropleft": +- cropleft = value + if name == "-qmin": + qmin = value + if name == "-qmax": +@@ -2526,7 +2588,6 @@ + command += "-aspect %s -r %s " % (aspectratio, fps) + if (deinterlace == 1): + command += "-deinterlace " +- command += "-croptop %s -cropright %s -cropbottom %s -cropleft %s " % (croptop, cropright, cropbottom, cropleft) + command += "-s %s -b %s -vcodec mpeg2video " % (outvideores, outvideobitrate) + command += "-qmin %s -qmax %s -qdiff %s " % (qmin, qmax, qdiff) + command += "-ab %s -ar %s -acodec %s " % (outaudiobitrate, outaudiosamplerate, outaudiocodec) +@@ -2543,7 +2604,7 @@ + if (not(doesFileExist(os.path.join(folder, "audout")) and doesFileExist(os.path.join(folder, "vidout")))): + fatalError("Waited too long for mythtranscode to create the fifos - giving up!!") + +- write("Running ffmpeg") ++ write("Running ffmpeg: %s" % command) + result = runCommand(command) + if result != 0: + os.kill(PID, signal.SIGKILL) +@@ -4724,7 +4785,7 @@ + elif file.attributes["type"].value=="recording": + mediafile = file.attributes["filename"].value + elif file.attributes["type"].value=="video": +- mediafile=os.path.join(videopath, file.attributes["filename"].value) ++ mediafile = os.path.join(getVideoPath(file.attributes["filename"].value), file.attributes["filename"].value) + elif file.attributes["type"].value=="file": + mediafile=file.attributes["filename"].value + else: +@@ -4834,8 +4895,7 @@ + starttime = -1 + usecutlist = -1 + +- encodeNuvToMPEG2(chanid, starttime, mediafile, os.path.join(folder, "newfile2.mpg"), folder, +- profile, usecutlist) ++ encodeNuvToMPEG2(chanid, starttime, mediafile, os.path.join(folder, "newfile2.mpg"), folder, profile, usecutlist) + mediafile = os.path.join(folder, 'newfile2.mpg') + else: + #we need to re-encode the file, make sure we get the right video/audio streams +@@ -4861,8 +4921,7 @@ + profile = defaultEncodingProfile + + #do the re-encode +- encodeVideoToMPEG2(mediafile, os.path.join(folder, "newfile2.mpg"), video, +- audio1, audio2, aspectratio, profile) ++ encodeVideoToMPEG2(mediafile, os.path.join(folder, "newfile2.mpg"), video, folder, audio1, audio2, aspectratio, profile) + mediafile = os.path.join(folder, 'newfile2.mpg') + + #remove the old mediafile that was run through mythtranscode +@@ -4936,7 +4995,7 @@ + elif file.attributes["type"].value=="recording": + mediafile = file.attributes["filename"].value + elif file.attributes["type"].value=="video": +- mediafile=os.path.join(videopath, file.attributes["filename"].value) ++ mediafile = os.path.join(getVideoPath(file.attributes["filename"].value), file.attributes["filename"].value) + elif file.attributes["type"].value=="file": + mediafile=file.attributes["filename"].value + else: +@@ -4988,8 +5047,7 @@ + starttime = -1 + usecutlist = -1 + +- encodeNuvToMPEG2(chanid, starttime, mediafile, os.path.join(folder, "newfile2.mpg"), folder, +- profile, usecutlist) ++ encodeNuvToMPEG2(chanid, starttime, mediafile, os.path.join(folder, "newfile2.mpg"), folder, profile, usecutlist) + mediafile = os.path.join(folder, 'newfile2.mpg') + else: + #we need to re-encode the file, make sure we get the right video/audio streams +@@ -5015,8 +5073,7 @@ + profile = defaultEncodingProfile + + #do the re-encode +- encodeVideoToMPEG2(mediafile, os.path.join(folder, "newfile2.mpg"), video, +- audio1, audio2, aspectratio, profile) ++ encodeVideoToMPEG2(mediafile, os.path.join(folder, "newfile2.mpg"), video, folder, audio1, audio2, aspectratio, profile) + mediafile = os.path.join(folder, 'newfile2.mpg') + + #remove an intermediate file diff --git a/abs/core/mythtv/stable-0.24/mythplugins/qt-4.7-buildfixes.diff b/abs/core/mythtv/stable-0.24/mythplugins/qt-4.7-buildfixes.diff deleted file mode 100644 index dcffebe..0000000 --- a/abs/core/mythtv/stable-0.24/mythplugins/qt-4.7-buildfixes.diff +++ /dev/null @@ -1,26 +0,0 @@ -Index: /branches/release-0-23-fixes/mythplugins/mythnetvision/mythnetvision/netsearch.cpp -=================================================================== ---- /branches/release-0-23-fixes/mythplugins/mythnetvision/mythnetvision/netsearch.cpp (revision 24265) -+++ /branches/release-0-23-fixes/mythplugins/mythnetvision/mythnetvision/netsearch.cpp (revision 26391) -@@ -45,7 +45,7 @@ - m_progress(NULL), m_busyPopup(NULL), - m_okPopup(NULL), m_popupStack(), -- m_netSearch(), m_currentSearch(NULL), -- m_currentGrabber(0), m_currentCmd(NULL), -- m_currentDownload(NULL), m_pagenum(0), -+ m_netSearch(), m_currentSearch(QString()), -+ m_currentGrabber(0), m_currentCmd(QString()), -+ m_currentDownload(QString()), m_pagenum(0), - m_lock(QMutex::Recursive) - { -Index: /branches/release-0-23-fixes/mythplugins/mythvideo/mythvideo/videodlg.h -=================================================================== ---- /branches/release-0-23-fixes/mythplugins/mythvideo/mythvideo/videodlg.h (revision 22916) -+++ /branches/release-0-23-fixes/mythplugins/mythvideo/mythvideo/videodlg.h (revision 26391) -@@ -151,5 +151,5 @@ - QString GetCoverImage(MythGenericTree *node); - QString GetFirstImage(MythGenericTree *node, QString type, -- QString gpnode = NULL, int levels = 0); -+ QString gpnode = QString(), int levels = 0); - QString GetImageFromFolder(Metadata *metadata); - QString GetScreenshot(MythGenericTree *node); -- cgit v0.12 From 23c37cfb9fe01afa6ce6fb2f1cfd6f4a0caea6a1 Mon Sep 17 00:00:00 2001 From: Britney Fransen Date: Fri, 21 Oct 2011 17:29:53 -0500 Subject: mythweb: update to latest --- abs/core/mythtv/stable-0.24/mythweb/PKGBUILD | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/abs/core/mythtv/stable-0.24/mythweb/PKGBUILD b/abs/core/mythtv/stable-0.24/mythweb/PKGBUILD index f5305d7..f0fb2f8 100644 --- a/abs/core/mythtv/stable-0.24/mythweb/PKGBUILD +++ b/abs/core/mythtv/stable-0.24/mythweb/PKGBUILD @@ -1,6 +1,6 @@ pkgname=mythweb pkgver=0.24 -pkgrel=14 +pkgrel=15 pkgdesc="Web interface for MythTV's backend" url="http://www.mythtv.org" license="GPL" -- cgit v0.12 From ef009094a09c522222c7380b4504b94bd13a4c3f Mon Sep 17 00:00:00 2001 From: Britney Fransen Date: Fri, 21 Oct 2011 17:54:28 -0500 Subject: flashplugin: update to latest --- abs/extra/community/flashplugin/PKGBUILD | 33 +++++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/abs/extra/community/flashplugin/PKGBUILD b/abs/extra/community/flashplugin/PKGBUILD index 7420bea..274c5e0 100644 --- a/abs/extra/community/flashplugin/PKGBUILD +++ b/abs/extra/community/flashplugin/PKGBUILD @@ -2,12 +2,12 @@ # Contributor: Andrea Scarpino pkgname=flashplugin -_licensefile='Reader_Player_AIR_WWEULA-Combined-20080204_1313.pdf' -pkgver=10.3.181.34 +_licensefile='PlatformClients_PC_WWEULA_Combined_20100108_1657.pdf' +pkgver=11.0.1.152 pkgrel=1 pkgdesc='Adobe Flash Player' url='http://get.adobe.com/flashplayer' -arch=('i686') # 'x86_64') +arch=('i686' 'x86_64') depends=('mozilla-common' 'libxt' 'gtk2' 'nss' 'curl' 'hicolor-icon-theme') optdepends=('libvdpau: GPU acceleration on Nvidia card') provides=('flashplayer') @@ -15,22 +15,37 @@ license=('custom') options=(!strip) install=flashplugin.install backup=(etc/adobe/mms.cfg) -source=('http://fpdownload.macromedia.com/get/flashplayer/current/install_flash_player_10_linux.tar.gz' - "http://www.adobe.com/products/eulas/pdfs/${_licensefile}" - mms.cfg) -md5sums=('26770108412158b710e633b06a71ca25' - '1636037610ee2aa35c5fb736a697b7e0' - 'f34aae6279b40e0bd2abfb0d9963d7b8') + +if [ "$CARCH" = "i686" ]; then + source=('http://fpdownload.macromedia.com/get/flashplayer/current/install_flash_player_11_linux.i386.tar.gz' + "http://www.adobe.com/products/eulas/pdfs/${_licensefile}" + mms.cfg) + md5sums=('34051edfcb78e6db14567a6c5f53e161' + '94ca2aecb409abfe36494d1a7ec7591d' + 'f34aae6279b40e0bd2abfb0d9963d7b8') +elif [ "$CARCH" = "x86_64" ]; then + source=('http://fpdownload.macromedia.com/get/flashplayer/current/install_flash_player_11_linux.x86_64.tar.gz' + "http://www.adobe.com/products/eulas/pdfs/${_licensefile}" + mms.cfg) + md5sums=('782952c5730caa4e4cbe7e1d9dfa6214' + '94ca2aecb409abfe36494d1a7ec7591d' + 'f34aae6279b40e0bd2abfb0d9963d7b8') +fi package () { cd ${srcdir} install -Dm755 libflashplayer.so "${pkgdir}/usr/lib/mozilla/plugins/libflashplayer.so" + install -Dm755 usr/lib/kde4/kcm_adobe_flash_player.so "${pkgdir}/usr/lib/kde4/kcm_adobe_flash_player.so" install -Dm755 usr/bin/flash-player-properties "${pkgdir}/usr/bin/flash-player-properties" for i in 16x16 22x22 24x24 32x32 48x48; do install -Dm644 usr/share/icons/hicolor/${i}/apps/flash-player-properties.png \ "${pkgdir}/usr/share/icons/hicolor/${i}/apps/flash-player-properties.png" done install -Dm644 usr/share/applications/flash-player-properties.desktop "${pkgdir}/usr/share/applications/flash-player-properties.desktop" + install -Dm644 usr/share/kde4/services/kcm_adobe_flash_player.desktop "${pkgdir}/usr/share/kde4/services/kcm_adobe_flash_player.desktop" install -Dm644 "${_licensefile}" "${pkgdir}/usr/share/licenses/${pkgname}/LICENSE.pdf" install -Dm644 ${srcdir}/mms.cfg "${pkgdir}/etc/adobe/mms.cfg" } +md5sums=('34051edfcb78e6db14567a6c5f53e161' + '94ca2aecb409abfe36494d1a7ec7591d' + 'f34aae6279b40e0bd2abfb0d9963d7b8') -- cgit v0.12 From 4bb32d7626181353f9f20e0a46d52e351e7f67ef Mon Sep 17 00:00:00 2001 From: Cecil Date: Tue, 25 Oct 2011 22:23:55 -0700 Subject: mythtv:Bumped to latest. --- abs/core/mythtv/stable-0.24/mythtv/PKGBUILD | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/abs/core/mythtv/stable-0.24/mythtv/PKGBUILD b/abs/core/mythtv/stable-0.24/mythtv/PKGBUILD index cf504eb..ce93cff 100755 --- a/abs/core/mythtv/stable-0.24/mythtv/PKGBUILD +++ b/abs/core/mythtv/stable-0.24/mythtv/PKGBUILD @@ -6,7 +6,7 @@ pkgname=mythtv pkgver=0.24 -pkgrel=24 +pkgrel=25 pkgdesc="A Homebrew PVR project" arch=('i686' 'x86_64') url="http://www.mythtv.org/" -- cgit v0.12