Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

YouTube API 3 Upload Video - Access not configured - Android

I am working on an Android app that records video and allows the user to upload it directly to YouTube using the YouTube Data API v3.

I have set up my app in Google's API console. Under services, I have YouTube Data API v3 enabled. Under API access I have both a section "Client ID for installed applications" (including a Client ID and Client Secret) and a section "Simple API Access" -> "Key for Android apps (with certificates)" (which includes an API key and an "Android Apps" section, which is left blank for now, i.e. allow all Android apps, but I have tried it with setting my android key).

I have based my code from a number of places, primarily:

https://developers.google.com/youtube/v3/code_samples/java#upload_a_video

and

https://code.google.com/p/google-api-java-client/source/browse/tasks-android-sample/src/main/java/com/google/api/services/samples/tasks/android/TasksSample.java?repo=samples

The upload initialises OK, starts the AsyncTask, but then I get an IOException thrown saying:

{
    "code": 403,
    "errors": [
        {
            "domain": "usageLimits",
            "message": "Access Not Configured",
            "reason": "accessNotConfigured"
        }
    ],
    "message": "Access Not Configured"
}

Similar SO posts suggest it is to do with my Google API console settings, but I can't find anything wrong. Any suggestions? I wonder if it is because I am not providing my client ID or secret anywhere ...

Thanks.

My code runs from a fragment containing a list of videos. The relevant sections are:

-- Init

public class UploadFragment extends Fragment {

    private static GoogleAccountCredential credential;
    private static final HttpTransport transport = AndroidHttp.newCompatibleTransport();
    private static final JsonFactory jsonFactory = new GsonFactory();
    public YouTube youtube;
    List<String> scopes = Lists.newArrayList(YouTubeScopes.YOUTUBE_UPLOAD);
    private static String VIDEO_FILE_FORMAT = "video/*";

    static final int REQUEST_GOOGLE_PLAY_SERVICES = 0;
    static final int REQUEST_AUTHORIZATION = 1;
    static final int REQUEST_ACCOUNT_PICKER = 2;

-- Setup credential and youtube

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        ...
        credential = googleAccountCredential(scopes);
        youtube = new YouTube.Builder(transport, jsonFactory, credential)
            .setApplicationName("MyAppName")
            .build();
        ...
    }

-- On button click, initiate upload

    @Override void onClick(View v) {
        ...
        if (hasGooglePlayServices()) {
            uploadYouTubeVideos();

        ...
    }

-- Build credential

    /**
     * Get the credential to authorize the installed application to access user's protected data.
     *
     * @param scopes list of scopes needed to run YouTube upload.
     */
    private static GoogleAccountCredential googleAccountCredential(List<String> scopes) throws Exception {
        credential = GoogleAccountCredential.usingOAuth2(context, scopes)
            .setSelectedAccountName(PreferenceManager.getAccountName());
        return credential;
    }

-- Request an account from the user

    /**
     * Fire intent to get user to choose account
     * Return to onActivityResult
     */
    private void chooseAccount() {
        startActivityForResult(credential.newChooseAccountIntent(), REQUEST_ACCOUNT_PICKER);
    }

-- On return from the user choosing and account -- / requesting authorization

    /**
     * Returns from chooseAccount and from request authorization
     */
    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        switch (requestCode) {
            case REQUEST_AUTHORIZATION:
                if (resultCode == Activity.RESULT_OK) {
                    uploadYouTubeVideos();
                } else {
                    chooseAccount();
                }
                break;
            case REQUEST_ACCOUNT_PICKER:
                if (resultCode == Activity.RESULT_OK && data != null && data.getExtras() != null) {
                    String accountName = data.getExtras().getString(AccountManager.KEY_ACCOUNT_NAME);
                    if (accountName != null) {
                        credential.setSelectedAccountName(accountName);
                        PreferenceManager.setAccountName(accountName);
                        uploadYouTubeVideos();
                    }
                }
                break;
        }
    }

-- Called multiple times depending on what information we have -- account, authorization, etc.

    /**
     * Uploads user selected video to the user's YouTube account using OAuth2
     * for authentication.
     *
     * @param videoFile file to be uploaded
     */
    public void uploadYouTubeVideos() {
        if (credential.getSelectedAccountName() == null) {
            chooseAccount();
        } else {
            File videoFile = getVideoFile();
            Insert videoInsert = prepareUpload(videoFile);
            new VideoUploadAsyncTask().execute(videoInsert);
        }
    }

-- Prepare the upload -- Puts everything together

    /**
     * Prepare upload. Just leaves execute to be run in AsyncTask.
     *
     * @param videoFile file to be uploaded
     * @return 
     */
    public Insert prepareUpload( File videoFile ) {
        try {
            // Add extra information to the video before uploading.
            Video videoObjectDefiningMetadata = new Video();

            // Set the video to public (default).
            VideoStatus status = new VideoStatus();
            status.setPrivacyStatus("public");
            videoObjectDefiningMetadata.setStatus(status);

            // We set a majority of the metadata with the VideoSnippet object.
            VideoSnippet snippet = new VideoSnippet();

            // Video file name.
            snippet.setTitle(videoFile.getName());
            snippet.setDescription("Test description");

            // Set keywords.
            List<String> tags = new ArrayList<String>();
            tags.add("test");
            snippet.setTags(tags);

            // Set completed snippet to the video object.
            videoObjectDefiningMetadata.setSnippet(snippet);

            InputStreamContent mediaContent = new InputStreamContent(
                    VIDEO_FILE_FORMAT, new BufferedInputStream(new FileInputStream(videoFile)));
            mediaContent.setLength(videoFile.length());

            /*
             * The upload command includes: 1. Information we want returned after file is successfully
             * uploaded. 2. Metadata we want associated with the uploaded video. 3. Video file itself.
             */
            YouTube.Videos.Insert videoInsert = youtube.videos()
                    .insert("snippet,statistics,status", videoObjectDefiningMetadata, mediaContent);

            // Set the upload type and add event listener.
            MediaHttpUploader uploader = videoInsert.getMediaHttpUploader();

            /*
             * Sets whether direct media upload is enabled or disabled. True = whole media content is
             * uploaded in a single request. False (default) = resumable media upload protocol to upload
             * in data chunks.
             */
            uploader.setDirectUploadEnabled(false);

            MediaHttpUploaderProgressListener progressListener = new MediaHttpUploaderProgressListener() {
                public void progressChanged(MediaHttpUploader uploader) throws IOException {
                    switch (uploader.getUploadState()) {
                        case INITIATION_STARTED:
                            Log.d(TAG, "Upload file: Initiation Started");
                            break;
                        case INITIATION_COMPLETE:
                            Log.d(TAG, "Upload file: Initiation Completed");
                            break;
                        case MEDIA_IN_PROGRESS:
                            Log.d(TAG, "Upload file: Upload in progress");
                            Log.d(TAG, "Upload file: Upload percentage: " + uploader.getProgress());
                            break;
                        case MEDIA_COMPLETE:
                            Log.d(TAG, "Upload file: Upload Completed!");
                            break;
                        case NOT_STARTED:
                            Log.d(TAG, "Upload file: Upload Not Started!");
                            break;
                    }
                }
            };
            uploader.setProgressListener(progressListener);

            return videoInsert;
        } catch (FileNotFoundException e) {
            Log.e(TAG, "File not found: " + e.getMessage());
            return null;
        } catch (IOException e) {
            Log.e(TAG, "IOException: " + e.getMessage());
            return null;
        }
    }

-- Require Google play services

    /** 
     * Pop up dialog requesting user to download Google Play Services.
     * Returns to onActivityResult
     */
    void showGooglePlayServicesAvailabilityErrorDialog(final int connectionStatusCode) {
        getActivity().runOnUiThread(new Runnable() {
            public void run() {
                Dialog dialog = 
                        GooglePlayServicesUtil.getErrorDialog(connectionStatusCode, getActivity(),
                        REQUEST_GOOGLE_PLAY_SERVICES);
                dialog.show();
            }
        });
    }

-- AsyncTask that runs execute on the upload

    public class VideoUploadAsyncTask extends AsyncTask<Insert, Void, Void> {
        @Override
        protected Void doInBackground( Insert... inserts ) {
            Insert videoInsert = inserts[0];
            try {
                Video returnVideo = videoInsert.execute();
            } catch (final GooglePlayServicesAvailabilityIOException availabilityException) {
                showGooglePlayServicesAvailabilityErrorDialog(
                        availabilityException.getConnectionStatusCode());
            } catch (UserRecoverableAuthIOException userRecoverableException) {
                startActivityForResult(
                        userRecoverableException.getIntent(), UploadFragment.REQUEST_AUTHORIZATION);
            } catch (IOException e) {
                Log.e(TAG, "IOException: " + e.getMessage());
            }
            return null;
        }
    }

}
like image 295
Jon G Avatar asked Aug 02 '13 17:08

Jon G


2 Answers

The answer provided by @Ibrahim was almost correct for me. What I needed to do was edit my API configuration. However, it was not the "Simple API access" section I needed to edit, it was the settings after clicking the button "Create another client Id".

Then I could select "Installed application" -> "Android". After inputting my package name and SHA1, and waiting 15 minutes, my app worked as expect. I also have the "Simple API access" set up. I am not sure if you need both or not.

like image 164
Jon G Avatar answered Sep 28 '22 09:09

Jon G


Yes, YouTube Direct Lite for Android is similar. You have to configure simple API access with your SHA1 key. Explains here.

like image 26
Ibrahim Ulukaya Avatar answered Sep 28 '22 09:09

Ibrahim Ulukaya