Decluttering My Digital Life


I recently came across a common problem which we all face, Subscribing non-active channels on Youtube.

I realized I was subscribed to hundreds of YouTube channels, many of which hadn’t posted a video in months….sometimes years! They were just cluttering my feed and wasting my attention.

The Goal: Find and automatically unsubscribe from any channel that hasn’t posted in the last 90 days.

This is exactly the kind of repetitive, data-heavy task that a general-purpose language like Python absolutely dominates. I put my love and respect for Python to work, building a simple automation script.

The High-Level Solution:

  1. Authentication: The script first used a secure Google API key to log in and get permission to view and manage my subscriptions.
  2. Data Fetching: It then made calls to the YouTube API to pull my entire subscription list.
  3. The Logic: For every single channel, the script performed a date check to see when their last video was published.
  4. Human Veto: Before making any permanent changes, it printed a clean list of all “inactive” channels and asked for a final “YES” confirmation from me.
  5. Execution: After my approval, it used the API one last time to systematically delete the inactive subscriptions.

The result? A streamlined YouTube feed and a little more peace of mind. It’s truly amazing what you can, accomplish with a little bit of code! Here is the code, which truly eased my life.

import os
from datetime import datetime, timedelta, timezone

from google_auth_oauthlib.flow import InstalledAppFlow
from googleapiclient.discovery import build
# Import HttpError specifically for the fix
from googleapiclient.errors import HttpError 

# --- CONFIGURATION ---
DAYS_INACTIVE_THRESHOLD = 90
SCOPES = ["https://www.googleapis.com/auth/youtube"]
CLIENT_SECRET_FILE = "client_secret.json"
# ---------------------

def get_authenticated_service():
    """Authenticates the user and returns the YouTube Data API service object."""
    # The first time this runs, it will open your browser for authentication
    flow = InstalledAppFlow.from_client_secrets_file(CLIENT_SECRET_FILE, SCOPES)
    credentials = flow.run_local_server(port=0)
    return build("youtube", "v3", credentials=credentials)

def list_my_subscriptions(youtube):
    """Retrieves all subscriptions for the authenticated user."""
    print("-> Fetching all current subscriptions...")
    subscriptions = []
    
    # Use 'pages' to handle large subscription lists
    next_page_token = None
    while True:
        request = youtube.subscriptions().list(
            part="snippet",
            mine=True,
            maxResults=50,
            pageToken=next_page_token
        )
        response = request.execute()
        
        for item in response.get("items", []):
            subscriptions.append(item)
            
        next_page_token = response.get("nextPageToken")
        if not next_page_token:
            break
            
    print(f"-> Found {len(subscriptions)} total subscriptions.")
    return subscriptions

def get_channel_activity(youtube, channel_id, channel_title):
    """
    Fetches the last upload date for a given channel.
    Includes robust error handling for missing channel data or playlists.
    """
    
    # 1. Fetch channel details to get the 'uploads' playlist ID
    request = youtube.channels().list(
        part="contentDetails",
        id=channel_id
    )
    response = request.execute()

    if not response.get("items"):
        print(f"Checking: {channel_title}... **Skipped (No Channel Data)**.")
        return None 

    try:
        # Get the ID of the playlist that holds the channel's uploads
        uploads_playlist_id = response["items"][0]["contentDetails"]["relatedPlaylists"]["uploads"]
    except KeyError:
        print(f"Checking: {channel_title}... **Skipped (No Uploads Playlist ID)**.")
        return None

    # 2. Fetch the most recent video from the uploads playlist
    try:
        request = youtube.playlistItems().list(
            part="snippet",
            playlistId=uploads_playlist_id,
            maxResults=1 # We only need the latest video
        )
        response = request.execute()
        
    # CRITICAL FIX: Handle the 404 error when the playlist cannot be found
    except HttpError as e:
        if e.resp.status == 404:
            # This handles channels where the 'uploads' playlist is inaccessible or not found
            print(f"Checking: {channel_title}... **Skipped (Uploads Playlist Not Found)**.")
            return None
        else:
            # Re-raise any other unexpected HTTP error
            raise e

    if response.get("items"):
        # The publishedAt field is the date the video was uploaded
        published_at_str = response["items"][0]["snippet"]["publishedAt"]
        # Convert the string to a datetime object
        return datetime.strptime(published_at_str, '%Y-%m-%dT%H:%M:%S%z')
    
    print(f"Checking: {channel_title}... **Skipped (No Videos Found in Playlist)**.")
    return None # No videos found in the playlist

def delete_subscription(youtube, subscription_id):
    """Deletes a single subscription by its ID."""
    youtube.subscriptions().delete(id=subscription_id).execute()
    
def main():
    if not os.path.exists(CLIENT_SECRET_FILE):
        print(f"ERROR: Client secret file '{CLIENT_SECRET_FILE}' not found.")
        print("Please follow the instructions to create and download the file.")
        return

    youtube = get_authenticated_service()
    all_subscriptions = list_my_subscriptions(youtube)
    
    # Calculate the cutoff date (90 days ago)
    cutoff_date = datetime.now(timezone.utc) - timedelta(days=DAYS_INACTIVE_THRESHOLD)
    inactive_subscriptions = []
    
    print("\n--- Checking Channel Activity ---")
    
    for sub in all_subscriptions:
        # Extract necessary IDs and title from the subscription object
        channel_id = sub["snippet"]["resourceId"]["channelId"]
        channel_title = sub["snippet"]["title"]
        subscription_id = sub["id"]
        
        # Pass the title to the function for better error logging
        last_activity = get_channel_activity(youtube, channel_id, channel_title)
        
        # Only proceed if we actually got a last activity date
        if last_activity is not None:
            if last_activity < cutoff_date:
                inactive_subscriptions.append({
                    "title": channel_title,
                    "sub_id": subscription_id,
                    "last_active": last_activity.date()
                })
                print(f"Checking: {channel_title}... INACTIVE. Last post: {last_activity.date()}")
            else:
                print(f"Checking: {channel_title}... Active. Last post: {last_activity.date()}")

    print("\n" + "="*50)
    print(f"SUMMARY: {len(inactive_subscriptions)} channels found inactive for > {DAYS_INACTIVE_THRESHOLD} days.")
    print("="*50)

    if not inactive_subscriptions:
        print("No inactive channels to remove. Exiting.")
        return

    # --- HUMAN REVIEW STEP ---
    print("\nChannels to be UN-SUBSCRIBED:")
    for i, item in enumerate(inactive_subscriptions):
        print(f"  {i+1}. **{item['title']}** (Last active: {item['last_active']})")
        
    confirmation = input("\nDo you want to DELETE these subscriptions? (Type 'YES' in all caps to proceed): ")
    
    if confirmation.upper() == 'YES':
        print("\n--- Deleting Subscriptions ---")
        for item in inactive_subscriptions:
            delete_subscription(youtube, item["sub_id"])
            print(f"Successfully deleted: {item['title']}")
        print("\n**Cleanup complete!**")
    else:
        print("Deletion cancelled by user. Exiting.")

if __name__ == "__main__":
    main()
Jitendra Chaudhary
Follow me
Latest posts by Jitendra Chaudhary (see all)
I hope you would find above article informative and  interesting. In case you need any further information, please feel free to comment , I shall try to reply the comment at the earliest. If you like this article, please like my Facebook page and advise/suggest me for more topics of your interest. Happy Coding!