import os
import time
import json
from audcon import log
from audcon.models import *
from audcon.modules import functions, enqueue
from gpm import formatting


def scan(full_scan = False):
    # Find the last run log entry
    last_run_log = RunLog.query.filter_by(service='scanner').order_by(RunLog.created_date.desc()).first()

    if last_run_log is not None:
        if last_run_log.status == 'running' and last_run_log.last_ran() < 86400:
            log.error('already running since {}'.format(last_run_log.created_date_local_tz()))
            return True

    # Add new RunLog entry
    log.debug('creating runlog entry')
    run_log_entry = RunLog(service='scanner', status='running')
    db.session.add(run_log_entry)
    db.session.commit()

    # Get the ID
    db.session.refresh(run_log_entry)
    log.debug('runlog entry: {}'.format(str(run_log_entry.id)))

    # Start processing
    log.info('fetching all files')
    all_files = functions.get_files(folders=app.config['MEDIA_FOLDER'], recursive=app.config['MEDIA_SCAN_RECURSIVELY'])

    # Find all files that have been created or modified since last run
    if last_run_log is not None and not full_scan:
        last_ran_in_s = last_run_log.last_ran()
        log.debug('last ran {}'.format(formatting.time_pretty(last_ran_in_s)))
    else:
        last_ran_in_s = -1
        log.warning('first time running, could take longer')

    files_to_scan = []
    for file in all_files:
        log.debug('file: {}'.format(file))
        # Skip os files e.g. ._
        if os.path.basename(file).startswith('._'):
            log.debug('skipping file: {}'.format(file))
        elif os.path.exists(file):
            # Find extension and file size
            ext = functions.get_file_extension(file)
            if ext in app.config['FILE_TYPES']: # Check if file exists
                log.info(ext.strip('.') + ': ' + file)

                file_last_updated_in_s = (time.time() - os.path.getmtime(file))
                # Check if the metadata was recently updated for files that are not in OK formats
                if ((last_ran_in_s > file_last_updated_in_s or last_ran_in_s == -1) and (ext in app.config[
                    'OK_A_FORMATS'])) or (ext not in app.config['OK_A_FORMATS']):
                    log.debug('file: {}'.format(file))
                    log.debug('file_last_updated_in_s: {}'.format(formatting.time_pretty(file_last_updated_in_s)))

                    # Find extension and file size
                    ext = functions.get_file_extension(file)
                    if ext in app.config['FILE_TYPES']:
                        log.info(ext.strip('.') + ': ' + file)
                        log.debug('adding to scan list')
                        files_to_scan.append(file)
                else:
                    log.debug('skipping as metadata has been recently updated: {}'.format(file))
            else:
                log.debug('skipped unsupported file type {}'.format(file))
        else:
            log.debug('skipping as file does not exist: {}'.format(file))

    log.info('{} files to scan'.format(len(files_to_scan)))

    # Now process this shortened list and update db
    for file in files_to_scan:
        log.info('file: {}'.format(file))

        try:
            ffprobe_success, ffprobe_output = functions.ffprobe(file)
            log.debug('ffprobe output {}'.format(ffprobe_output))
        except Exception as e:
            log.error('error occcurred running ffprobe')
            log.error(e)
        else:
            if ffprobe_success:
                file_meta = json.loads(ffprobe_output)
                log.info('format: {}'.format(file_meta.get('format').get('format_name')))

                # Check if we have this file in our database and find out if update/insert
                operation = 'update'
                record = Media.query.filter_by(media_file_path=file).first()
                if record is None:
                    record = Media()
                    record.media_file_path = file
                    operation = 'insert'

                record.media_fsize = os.stat(file).st_size
                record.media_format = file_meta.get('format').get('format_name')

                # Set media title from Tag if available, if not use the file name
                record.media_title = file_meta.get('format').get('tags').get('TITLE')
                if record.media_title is None:
                    record.media_title = functions.get_file_name_without_extension(file)

                # Find number of streams and acodec details
                record.media_streams_count = len(file_meta.get('streams'))
                record.media_a_streams_count = 0
                record.media_v_streams_count = 0
                record.media_s_streams_count = 0
                record.media_o_streams_count = 0
                record.media_a_codec = None
                record.media_a_sample_fmt = None
                record.media_a_sample_rate = None
                record.media_a_channels = None
                record.media_a_channel_layout = None
                record.media_a_bitrate = None
                record.media_full_meta = ffprobe_output

                for stream in file_meta.get('streams'):
                    stream_type = stream.get('codec_type')
                    if stream_type == 'audio':
                        record.media_a_codec = stream.get('codec_name')
                        record.media_a_streams_count += 1

                        # Get only for first stream
                        if record.media_a_streams_count == 1:
                            log.debug('extracting acodec attributes')
                            record.media_a_sample_fmt = stream.get('sample_fmt')
                            record.media_a_sample_rate = stream.get('sample_rate')
                            record.media_a_channels = stream.get('channels')
                            record.media_a_channel_layout = stream.get('channel_layout')
                            record.media_a_bitrate = stream.get('bit_rate')

                            # If bitrate is not there in the stream, get it from upper level format tag
                            if stream.get('bit_rate') is None:
                                record.media_a_bitrate = file_meta.get('format').get('bit_rate')
                        else:
                            log.warning('ignoring this stream: {}'.format(file.get('media_file_path')))
                    elif stream_type == 'video':
                        log.debug('video stream found: {}'.format(file))
                        record.media_v_streams_count += 1
                    elif stream_type == 'subtitle':
                        log.debug('subtitle stream found: {}'.format(file))
                        record.media_s_streams_count += 1
                    else:
                        log.debug('other stream found: {}'.format(file))
                        record.media_o_streams_count += 1

                # Update database
                if operation == 'insert':
                    db.session.add(record)

                db.session.commit()
                db.session.refresh(record)

                # Enqueue
                not_supported = ''
                if ('.' + record.media_a_codec) not in app.config['OK_A_FORMATS']:
                    not_supported = '**not supported'
                    enqueue.enqueue(file)

                log.info('codec: {} {}'.format(record.media_a_codec, not_supported))
            else:
                log.error('error occurred while running ffprobe')

    # Remove dead entries
    log.info('checking for dead entries')
    media = Media.query.filter_by(isdeleted=False).all()
    dead_entries = 0
    for file in media:
        if not os.path.exists(file.media_file_path):
            file.isdeleted = True
            dead_entries += 1

    log.info('found {} dead entries'.format(dead_entries))
    db.session.commit()

    # Update RunLog
    params = {
        'files_scanned': len(files_to_scan),
        'full_scan': full_scan,
        'dead_entries': dead_entries
    }

    run_log_entry.status='complete'
    run_log_entry.params = params
    db.session.commit()




import os
import time
import json
from audcon import log
from audcon.models import *
from audcon.modules import functions, enqueue
from gpm import formatting


def scan(full_scan = False):
    # Find the last run log entry
    last_run_log = RunLog.query.filter_by(service='scanner').order_by(RunLog.created_date.desc()).first()

    if last_run_log is not None:
        if last_run_log.status == 'running' and last_run_log.last_ran() < 86400:
            log.error('already running since {}'.format(last_run_log.created_date_local_tz()))
            return True

    # Add new RunLog entry
    log.debug('creating runlog entry')
    run_log_entry = RunLog(service='scanner', status='running')
    db.session.add(run_log_entry)
    db.session.commit()

    # Get the ID
    db.session.refresh(run_log_entry)
    log.debug('runlog entry: {}'.format(str(run_log_entry.id)))

    # Start processing
    log.info('fetching all files')
    all_files = functions.get_files(folders=app.config['MEDIA_FOLDER'], recursive=app.config['MEDIA_SCAN_RECURSIVELY'])

    # Find all files that have been created or modified since last run
    if last_run_log is not None and not full_scan:
        last_ran_in_s = last_run_log.last_ran()
        log.debug('last ran {}'.format(formatting.time_pretty(last_ran_in_s)))
    else:
        last_ran_in_s = -1
        log.warning('first time running, could take longer')

    files_to_scan = []
    for file in all_files:
        log.debug('file: {}'.format(file))
        # Skip os files e.g. ._
        if os.path.basename(file).startswith('._'):
            log.debug('skipping file: {}'.format(file))
        elif os.path.exists(file):
            # Find extension and file size
            ext = functions.get_file_extension(file)
            if ext in app.config['FILE_TYPES']: # Check if file exists
                log.info(ext.strip('.') + ': ' + file)

                file_last_updated_in_s = (time.time() - os.path.getmtime(file))
                # Check if the metadata was recently updated for files that are not in OK formats
                if ((last_ran_in_s > file_last_updated_in_s or last_ran_in_s == -1) and (ext in app.config[
                    'OK_A_FORMATS'])) or (ext not in app.config['OK_A_FORMATS']):
                    log.debug('file: {}'.format(file))
                    log.debug('file_last_updated_in_s: {}'.format(formatting.time_pretty(file_last_updated_in_s)))

                    # Find extension and file size
                    ext = functions.get_file_extension(file)
                    if ext in app.config['FILE_TYPES']:
                        log.info(ext.strip('.') + ': ' + file)
                        log.debug('adding to scan list')
                        files_to_scan.append(file)
                else:
                    log.debug('skipping as metadata has been recently updated: {}'.format(file))
            else:
                log.debug('skipped unsupported file type {}'.format(file))
        else:
            log.debug('skipping as file does not exist: {}'.format(file))

    log.info('{} files to scan'.format(len(files_to_scan)))

    # Now process this shortened list and update db
    for file in files_to_scan:
        log.info('file: {}'.format(file))

        try:
            ffprobe_success, ffprobe_output = functions.ffprobe(file)
            log.debug('ffprobe output {}'.format(ffprobe_output))
        except Exception as e:
            log.error('error occcurred running ffprobe')
            log.error(e)
        else:
            if ffprobe_success:
                file_meta = json.loads(ffprobe_output)
                log.info('format: {}'.format(file_meta.get('format').get('format_name')))

                # Check if we have this file in our database and find out if update/insert
                operation = 'update'
                record = Media.query.filter_by(media_file_path=file).first()
                if record is None:
                    record = Media()
                    record.media_file_path = file
                    operation = 'insert'

                record.media_fsize = os.stat(file).st_size
                record.media_format = file_meta.get('format').get('format_name')

                # Set media title from Tag if available, if not use the file name
                record.media_title = file_meta.get('format').get('tags').get('TITLE')
                if record.media_title is None:
                    record.media_title = functions.get_file_name_without_extension(file)

                # Find number of streams and acodec details
                record.media_streams_count = len(file_meta.get('streams'))
                record.media_a_streams_count = 0
                record.media_v_streams_count = 0
                record.media_s_streams_count = 0
                record.media_o_streams_count = 0
                record.media_a_codec = None
                record.media_a_sample_fmt = None
                record.media_a_sample_rate = None
                record.media_a_channels = None
                record.media_a_channel_layout = None
                record.media_a_bitrate = None
                record.media_full_meta = ffprobe_output

                for stream in file_meta.get('streams'):
                    stream_type = stream.get('codec_type')
                    if stream_type == 'audio':
                        record.media_a_codec = stream.get('codec_name')
                        record.media_a_streams_count += 1

                        # Get only for first stream
                        if record.media_a_streams_count == 1:
                            log.debug('extracting acodec attributes')
                            record.media_a_sample_fmt = stream.get('sample_fmt')
                            record.media_a_sample_rate = stream.get('sample_rate')
                            record.media_a_channels = stream.get('channels')
                            record.media_a_channel_layout = stream.get('channel_layout')
                            record.media_a_bitrate = stream.get('bit_rate')

                            # If bitrate is not there in the stream, get it from upper level format tag
                            if stream.get('bit_rate') is None:
                                record.media_a_bitrate = file_meta.get('format').get('bit_rate')
                        else:
                            log.warning('ignoring this stream: {}'.format(file.get('media_file_path')))
                    elif stream_type == 'video':
                        log.debug('video stream found: {}'.format(file))
                        record.media_v_streams_count += 1
                    elif stream_type == 'subtitle':
                        log.debug('subtitle stream found: {}'.format(file))
                        record.media_s_streams_count += 1
                    else:
                        log.debug('other stream found: {}'.format(file))
                        record.media_o_streams_count += 1

                # Update database
                if operation == 'insert':
                    db.session.add(record)

                db.session.commit()
                db.session.refresh(record)

                # Enqueue
                not_supported = ''
                if ('.' + record.media_a_codec) not in app.config['OK_A_FORMATS']:
                    not_supported = '**not supported'
                    enqueue.enqueue(file)

                log.info('codec: {} {}'.format(record.media_a_codec, not_supported))
            else:
                log.error('error occurred while running ffprobe')

    # Remove dead entries
    log.info('checking for dead entries')
    media = Media.query.filter_by(isdeleted=False).all()
    dead_entries = 0
    for file in media:
        if not os.path.exists(file.media_file_path):
            file.isdeleted = True
            dead_entries += 1

    log.info('found {} dead entries'.format(dead_entries))
    db.session.commit()

    # Update RunLog
    params = {
        'files_scanned': len(files_to_scan),
        'full_scan': full_scan,
        'dead_entries': dead_entries
    }

    run_log_entry.status='complete'
    run_log_entry.params = params
    db.session.commit()




# Find the last run log entry # Add new RunLog entry # Get the ID # Start processing # Find all files that have been created or modified since last run # Skip os files e.g. ._ # Find extension and file size # Check if file exists # Check if the metadata was recently updated for files that are not in OK formats # Find extension and file size # Now process this shortened list and update db # Check if we have this file in our database and find out if update/insert # Set media title from Tag if available, if not use the file name # Find number of streams and acodec details # Get only for first stream # If bitrate is not there in the stream, get it from upper level format tag # Update database # Enqueue # Remove dead entries # Update RunLog