PageSourceSearch

https://www.vdocipher.com/docs/assets/js/49216cda.1e28d682.js

js vdocipher.com collected 2026-09-24 17:16:15 UTC 10,056 bytes, 1 lines download raw bytes

1"use strict";(self.webpackChunkdocusaurus=self.webpackChunkdocusaurus||[]).push([[1533],{18912:(e,n,o)=>{o.r(n),o.d(n,{assets:()=>s,contentTitle:()=>i,default:()=>f,frontMatter:()=>l,metadata:()=>d,toc:()=>r});var t=o(74848),a=o(28453);const l={title:"Downloading videos for offline"},i=void 0,d={id:"mobile/ios/offline",title:"Downloading videos for offline",description:"Generating OTP for Offline Playback",source:"@site/docs/mobile/ios/offline.md",sourceDirName:"mobile/ios",slug:"/mobile/ios/offline",permalink:"/docs/mobile/ios/offline",draft:!1,unlisted:!1,tags:[],version:"current",frontMatter:{title:"Downloading videos for offline"},sidebar:"mobile",previous:{title:"Getting started",permalink:"/docs/mobile/ios/getting-started"},next:{title:"SwiftUI Integration",permalink:"/docs/mobile/ios/swiftUI"}},s={},r=[{value:"Generating OTP for Offline Playback",id:"generating-otp-for-offline-playback",level:2},{value:"Setting up offline",id:"setting-up-offline",level:2},{value:"1. Notification handlers",id:"1-notification-handlers",level:3},{value:"2. Handler functions to update the UI about progress and download completions",id:"2-handler-functions-to-update-the-ui-about-progress-and-download-completions",level:3},{value:"AssetDownloadProgress",id:"assetdownloadprogress",level:4},{value:"AssetDownloadStateChanged",id:"assetdownloadstatechanged",level:4},{value:"AssetDownloadFailed",id:"assetdownloadfailed",level:4},{value:"Sample handler code",id:"sample-handler-code",level:4},{value:"3. Call the download APIs from the UI",id:"3-call-the-download-apis-from-the-ui",level:3},{value:"4. Call the <code>playOffline()</code> method to start offline playback",id:"4-call-the-playoffline-method-to-start-offline-playback",level:3},{value:"Get list of downloads",id:"get-list-of-downloads",level:2}];function c(e){const n={a:"a",admonition:"admonition",code:"code",em:"em",h2:"h2",h3:"h3",h4:"h4",p:"p",pre:"pre",...(0,a.R)(),...e.components};return(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(n.h2,{id:"generating-otp-for-offline-playback",children:"Generating OTP for Offline Playback"}),"\n",(0,t.jsxs)(n.p,{children:["To initialize video playback on website or app, your backend has to generate\nan ",(0,t.jsx)(n.code,{children:"OTP"}),"+",(0,t.jsx)(n.code,{children:"playbackInfo"})," using the VdoCipher API. The regular OTP does\nnot have permission to be persisted offline. To enable offline playback you\nwill need to send additional parameters while making the API call for the OTP."]}),"\n",(0,t.jsxs)(n.p,{children:["You can specify the time period for which the offline download will be\navailable for playback. The time period of validity is called the\n",(0,t.jsx)(n.em,{children:"rental duration"}),". Beyond the rental duration the license will expire, and the\ndownloaded file will no longer play."]}),"\n",(0,t.jsx)(n.p,{children:(0,t.jsx)(n.a,{href:"/docs/server/playbackauth/offline",children:"How to generate offline OTP \xbb"})}),"\n",(0,t.jsxs)(n.p,{children:["You can find more details on the OTP-based playback mechanism at the\n",(0,t.jsx)(n.a,{href:"https://www.vdocipher.com/page/api",children:"API page"}),"."]}),"\n",(0,t.jsx)(n.h2,{id:"setting-up-offline",children:"Setting up offline"}),"\n",(0,t.jsx)(n.admonition,{title:"Requires Fairplay setup",type:"note",children:(0,t.jsx)(n.p,{children:"Offline video playback is only available if you have set up Fairplay DRM and\nconfigured it with your VdoCipher account."})}),"\n",(0,t.jsx)(n.h3,{id:"1-notification-handlers",children:"1. Notification handlers"}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-swift",children:"// listen to the download events\nNotificationCenter.default.addObserver(\n     self,\n     selector: #selector(handleDownloadProgress(_:)),\n     name: .AssetDownloadProgress, object: nil\n)\n\nNotificationCenter.default.addObserver(\n    self,\n    selector: #selector(handleDownloadStateChange(_:)),\n    name: .AssetDownloadStateChanged, object: nil\n)\n\nNotificationCenter.default.addObserver(\n    self,\n    selector: #selector(handleDownloadFailed(_:)),\n    name: .AssetDownloadFailed, object: nil\n)\n"})}),"\n",(0,t.jsx)(n.h3,{id:"2-handler-functions-to-update-the-ui-about-progress-and-download-completions",children:"2. Handler functions to update the UI about progress and download completions"}),"\n",(0,t.jsx)(n.p,{children:"The notification received from the above will be of the following structures."}),"\n",(0,t.jsx)(n.h4,{id:"assetdownloadprogress",children:"AssetDownloadProgress"}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-json",children:'{\n  "videoId": "____",\n  "percentDownlaod": 0.2\n}\n'})}),"\n",(0,t.jsx)(n.h4,{id:"assetdownloadstatechanged",children:"AssetDownloadStateChanged"}),"\n",(0,t.jsxs)(n.p,{children:["The downloadState will be one of the enum from ",(0,t.jsx)(n.code,{children:"VdoAsset.DownloadState"}),"."]}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-json",children:'{\n  "videoId": "___",\n  "downloadState": "downloaded"\n}\n'})}),"\n",(0,t.jsx)(n.h4,{id:"assetdownloadfailed",children:"AssetDownloadFailed"}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-json",children:'{\n  "videoId": "____",\n  "message": "___ __ __",\n  "code": xxxx\n}\n'})}),"\n",(0,t.jsx)(n.admonition,{type:"note",children:(0,t.jsxs)(n.p,{children:["Do not use string for the keys. Use the ",(0,t.jsx)(n.code,{children:"VdoAsset.Keys"})," struct for the keys of\nthe ",(0,t.jsx)(n.code,{children:"userInfo"}),". Check the examples below for an example."]})}),"\n",(0,t.jsx)(n.h4,{id:"sample-handler-code",children:"Sample handler code"}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-swift",children:'// MARK: Notification handlers\n\n@objc func handleDownloadStateChange(_ notification: Notification) {\n    print("handle download state change")\n    let notice = notification.userInfo!\n    guard videoId == notice[VdoAsset.Keys.videoId] as? String else {\n        return\n    }\n    guard let stateRawValue = notice[VdoAsset.Keys.downloadState] as? String,\n          let newState = VdoAsset.DownloadState(rawValue: (stateRawValue)) else {\n        return\n    }\n    print("state change: \\(newState)")\n    DispatchQueue.main.async {\n        self.updateUI(downloadState: newState)\n    }\n}\n\n@objc func handleDownloadProgress(_ notification: Notification) {\n    print("handle download progress")\n    let notice = notification.userInfo!\n    guard videoId == notice[VdoAsset.Keys.videoId] as? String else {\n        return\n    }\n    guard let percent = notice[VdoAsset.Keys.percentDownloaded] as? Double else {\n        return\n    }\n    print("progress percent: \\(percent)")\n    DispatchQueue.main.async {\n        self.updateUI(downloadState: .downloading, percentDownload: percent)\n    }\n}\n\n@objc func handleDownloadFailed(_ notification: Notification) {\n    print("handle download failed")\n    let notice = notification.userInfo!\n    if let message = notice[VdoAsset.Keys.message], let code = notice[VdoAsset.Keys.code]\n    {\n        print("Download Failed with message:\\(message) code:\\(code)")\n    }\n}\n'})}),"\n",(0,t.jsx)(n.h3,{id:"3-call-the-download-apis-from-the-ui",children:"3. Call the download APIs from the UI"}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-swift",children:'@IBAction func downloadAction(_ sender: Any) {\n    guard let asset = asset, let button = sender as? UIButton, let buttonText = button.currentTitle else {\n        return print("not ready for playback or action not defined")\n    }\n    asset.startDownload(otp: otp, playbackInfo: playbackInfo)\n}\n\n@IBAction func cancelAction(_ sender: Any) {\n    asset.deleteDownload()\n}\n\n@IBAction func deleteAction(_ sender: Any) {\n    asset.deleteDownload()\n}\n\n@IBAction func pauseAction(_ sender: Any) {\n    asset.pauseDownload()\n}\n\n@IBAction func resumeAction(_ sender: Any) {\n    asset.resumeDownload()\n}\n'})}),"\n",(0,t.jsxs)(n.h3,{id:"4-call-the-playoffline-method-to-start-offline-playback",children:["4. Call the ",(0,t.jsx)(n.code,{children:"playOffline()"})," method to start offline playback"]}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-swift",children:'@IBAction func playOffline(_ sender: Any) {\n    guard let asset = asset else {\n        return print("not ready for offline playback")\n    }\n    if asset.downloadState != .downloaded {\n        return print("not downloaded yet")\n    }\n    asset.playOffline()\n}\n'})}),"\n",(0,t.jsx)(n.h2,{id:"get-list-of-downloads",children:"Get list of downloads"}),"\n",(0,t.jsxs)(n.p,{children:["Call the ",(0,t.jsx)(n.code,{children:"getListOfDownloads()"})," method to get list of downloads. It return array of\ntype 'DownloadInfo' which has all the basic 
1information about a downloaded asset"]}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-swift",children:"    let allDownloads = VdoCipher.getListOfDownloads()\n}\n"})}),"\n",(0,t.jsx)(n.p,{children:"Alternatively, you can filter your results using 'DownloadInfoFilter'.\nYou can get 'DownloadInfo' of particular set of videoIds and also for particular 'DownloadState'."}),"\n",(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-swift",children:'    let videoIds = ["vid1", "vid2", "vid3"]\n    let downloads = VdoCipher.getListOfDownloads(filterBy: DownloadInfoFilter.init(videoId: videoIds, downloadState: [.paused, .downloading]))\n}\n'})})]})}function f(e={}){const{wrapper:n}={...(0,a.R)(),...e.components};return n?(0,t.jsx)(n,{...e,children:(0,t.jsx)(c,{...e})}):c(e)}},28453:(e,n,o)=>{o.d(n,{R:()=>i,x:()=>d});var t=o(96540);const a={},l=t.createContext(a);function i(e){const n=t.useContext(l);return t.useMemo(function(){return"function"==typeof e?e(n):{...n,...e}},[n,e])}function d(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(a):e.components||a:i(e.components),t.createElement(l.Provider,{value:n},e.children)}}}]);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.